diff --git a/contract/src/constants.lua b/contract/src/constants.lua index 1c4a8c41..e42ced1a 100644 --- a/contract/src/constants.lua +++ b/contract/src/constants.lua @@ -1,5 +1,14 @@ local constants = {} +-- OIP +constants.epochTimeLength = 24 * 60 * 60 * 1000 -- One day of miliseconds +constants.epochZeroStartTimestamp = 0 +constants.EPOCH_DISTRIBUTION_DELAY = 30 * 60 * 1000 -- 30 minutes of miliseconds +constants.TENURE_WEIGHT_DAYS = 180 +constants.TENURE_WEIGHT_PERIOD = constants.TENURE_WEIGHT_DAYS * 24 * 60 * 60 * 1000 +constants.MAX_TENURE_WEIGHT = 4 +constants.MAXIMUM_OBSERVERS_PER_EPOCH = 50 + -- GAR constants.DEFAULT_UNDERNAME_COUNT = 10 constants.DEADLINE_DURATION_MS = 60 * 60 * 1000 -- One hour of miliseconds diff --git a/contract/src/crypto/cipher/aes.lua b/contract/src/crypto/cipher/aes.lua new file mode 100644 index 00000000..5ed1f5d4 --- /dev/null +++ b/contract/src/crypto/cipher/aes.lua @@ -0,0 +1,142 @@ +local Stream = require(".crypto.util.stream") +local Hex = require(".crypto.util.hex") +local Array = require(".crypto.util.array") + +-- Ciphers +local AES128Cipher = require(".crypto.cipher.aes128") +local AES192Cipher = require(".crypto.cipher.aes192") +local AES256Cipher = require(".crypto.cipher.aes256") + +-- Modes +local CBCMode = require(".crypto.cipher.mode.cbc") +local ECBMode = require(".crypto.cipher.mode.ecb") +local CFBMode = require(".crypto.cipher.mode.cfb") +local OFBMode = require(".crypto.cipher.mode.ofb") +local CTRMode = require(".crypto.cipher.mode.ctr") + +-- Padding +local ZeroPadding = require(".crypto.padding.zero") + +local public = {} + +local getBlockCipher = function(keyLength) + if keyLength == 128 then + return AES128Cipher + elseif keyLength == 192 then + return AES192Cipher + elseif keyLength == 256 then + return AES256Cipher + elseif keyLength == nil then + return AES128Cipher + else + return nil + end +end + +local getMode = function(mode) + if mode == "CBC" then + return CBCMode + elseif mode == "ECB" then + return ECBMode + elseif mode == "CFB" then + return CFBMode + elseif mode == "OFB" then + return OFBMode + elseif mode == "CTR" then + return CTRMode + else + return nil + end +end + + +--- Encrypts the given data using AES encryption. +--- @param data string - The data to be encrypted. +--- @param key string - The key to use for encryption. +--- @param iv? string (optional) - The initialization vector to use for encryption. Defaults to 16 null bytes. +--- @param mode? string (optional) - The mode to use for encryption. Defaults to "CBC". +--- @param keyLength? number (optional) - The length of the key to use for encryption. Defaults to 128. +--- @returns table - A table containing the encrypted data in bytes, hex, and string formats. +public.encrypt = function(data, key, iv, mode, keyLength) + local d = Array.fromString(data) + local k = Array.fromString(key) + local _iv = iv ~= nil and Array.fromString(iv) or Array.fromHex("00000000000000000000000000000000") + + local cipherMode = getMode(mode) or CBCMode + local blockCipher = getBlockCipher(keyLength) or AES128Cipher + + local cipher = cipherMode.Cipher() + .setKey(k) + .setBlockCipher(blockCipher) + .setPadding(ZeroPadding); + + + local cipherOutput = cipher + .init() + .update(Stream.fromArray(_iv)) + .update(Stream.fromArray(d)) + .finish() + + local results = {} + + results.asBytes = function() + return cipherOutput.asBytes() + end + + results.asHex = function() + return cipherOutput.asHex() + end + + results.asString = function() + return cipherOutput.asString() + end + + return results +end + +--- Decrypts the given data using AES decryption. +--- @param cipher string - The hex encoded cipher to be decrypted. +--- @param key string - The key to use for decryption. +--- @param iv? string (optional) - The initialization vector to use for decryption. Defaults to 16 null bytes. +--- @param mode? string (optional) - The mode to use for decryption. Defaults to "CBC". +--- @param keyLength? number (optional) - The length of the key to use for decryption. Defaults to 128. +public.decrypt = function(cipher, key, iv, mode, keyLength) + local cipherText = Array.fromHex(cipher) + local k = Array.fromString(key) + local _iv = iv ~= nil and Array.fromString(iv) or Array.fromHex("00000000000000000000000000000000") + + local cipherMode = getMode(mode) or CBCMode + local blockCipher = getBlockCipher(keyLength) or AES128Cipher + + + local decipher = cipherMode.Decipher() + .setKey(k) + .setBlockCipher(blockCipher) + .setPadding(ZeroPadding); + + + local plainOutput = decipher + .init() + .update(Stream.fromArray(_iv)) + .update(Stream.fromArray(cipherText)) + .finish() + + local results = {} + + results.asBytes = function() + return plainOutput.asBytes() + end + + results.asHex = function() + return plainOutput.asHex() + end + + results.asString = function() + return plainOutput.asString() + end + + return results +end + + +return public diff --git a/contract/src/crypto/cipher/aes128.lua b/contract/src/crypto/cipher/aes128.lua new file mode 100644 index 00000000..061598e0 --- /dev/null +++ b/contract/src/crypto/cipher/aes128.lua @@ -0,0 +1,415 @@ +local Array = require(".crypto.util.array"); +local Bit = require(".crypto.util.bit"); + +local XOR = Bit.bxor; + +local SBOX = { + [0] = 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16}; + +local ISBOX = { + [0] = 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D}; + +local ROW_SHIFT = { 1, 6, 11, 16, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, }; +local IROW_SHIFT = { 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3, 16, 13, 10, 7, 4, }; + +local ETABLE = { + [0] = 0x01, 0x03, 0x05, 0x0F, 0x11, 0x33, 0x55, 0xFF, 0x1A, 0x2E, 0x72, 0x96, 0xA1, 0xF8, 0x13, 0x35, + 0x5F, 0xE1, 0x38, 0x48, 0xD8, 0x73, 0x95, 0xA4, 0xF7, 0x02, 0x06, 0x0A, 0x1E, 0x22, 0x66, 0xAA, + 0xE5, 0x34, 0x5C, 0xE4, 0x37, 0x59, 0xEB, 0x26, 0x6A, 0xBE, 0xD9, 0x70, 0x90, 0xAB, 0xE6, 0x31, + 0x53, 0xF5, 0x04, 0x0C, 0x14, 0x3C, 0x44, 0xCC, 0x4F, 0xD1, 0x68, 0xB8, 0xD3, 0x6E, 0xB2, 0xCD, + 0x4C, 0xD4, 0x67, 0xA9, 0xE0, 0x3B, 0x4D, 0xD7, 0x62, 0xA6, 0xF1, 0x08, 0x18, 0x28, 0x78, 0x88, + 0x83, 0x9E, 0xB9, 0xD0, 0x6B, 0xBD, 0xDC, 0x7F, 0x81, 0x98, 0xB3, 0xCE, 0x49, 0xDB, 0x76, 0x9A, + 0xB5, 0xC4, 0x57, 0xF9, 0x10, 0x30, 0x50, 0xF0, 0x0B, 0x1D, 0x27, 0x69, 0xBB, 0xD6, 0x61, 0xA3, + 0xFE, 0x19, 0x2B, 0x7D, 0x87, 0x92, 0xAD, 0xEC, 0x2F, 0x71, 0x93, 0xAE, 0xE9, 0x20, 0x60, 0xA0, + 0xFB, 0x16, 0x3A, 0x4E, 0xD2, 0x6D, 0xB7, 0xC2, 0x5D, 0xE7, 0x32, 0x56, 0xFA, 0x15, 0x3F, 0x41, + 0xC3, 0x5E, 0xE2, 0x3D, 0x47, 0xC9, 0x40, 0xC0, 0x5B, 0xED, 0x2C, 0x74, 0x9C, 0xBF, 0xDA, 0x75, + 0x9F, 0xBA, 0xD5, 0x64, 0xAC, 0xEF, 0x2A, 0x7E, 0x82, 0x9D, 0xBC, 0xDF, 0x7A, 0x8E, 0x89, 0x80, + 0x9B, 0xB6, 0xC1, 0x58, 0xE8, 0x23, 0x65, 0xAF, 0xEA, 0x25, 0x6F, 0xB1, 0xC8, 0x43, 0xC5, 0x54, + 0xFC, 0x1F, 0x21, 0x63, 0xA5, 0xF4, 0x07, 0x09, 0x1B, 0x2D, 0x77, 0x99, 0xB0, 0xCB, 0x46, 0xCA, + 0x45, 0xCF, 0x4A, 0xDE, 0x79, 0x8B, 0x86, 0x91, 0xA8, 0xE3, 0x3E, 0x42, 0xC6, 0x51, 0xF3, 0x0E, + 0x12, 0x36, 0x5A, 0xEE, 0x29, 0x7B, 0x8D, 0x8C, 0x8F, 0x8A, 0x85, 0x94, 0xA7, 0xF2, 0x0D, 0x17, + 0x39, 0x4B, 0xDD, 0x7C, 0x84, 0x97, 0xA2, 0xFD, 0x1C, 0x24, 0x6C, 0xB4, 0xC7, 0x52, 0xF6, 0x01}; + +local LTABLE = { + [0] = 0x00, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1A, 0xC6, 0x4B, 0xC7, 0x1B, 0x68, 0x33, 0xEE, 0xDF, 0x03, + 0x64, 0x04, 0xE0, 0x0E, 0x34, 0x8D, 0x81, 0xEF, 0x4C, 0x71, 0x08, 0xC8, 0xF8, 0x69, 0x1C, 0xC1, + 0x7D, 0xC2, 0x1D, 0xB5, 0xF9, 0xB9, 0x27, 0x6A, 0x4D, 0xE4, 0xA6, 0x72, 0x9A, 0xC9, 0x09, 0x78, + 0x65, 0x2F, 0x8A, 0x05, 0x21, 0x0F, 0xE1, 0x24, 0x12, 0xF0, 0x82, 0x45, 0x35, 0x93, 0xDA, 0x8E, + 0x96, 0x8F, 0xDB, 0xBD, 0x36, 0xD0, 0xCE, 0x94, 0x13, 0x5C, 0xD2, 0xF1, 0x40, 0x46, 0x83, 0x38, + 0x66, 0xDD, 0xFD, 0x30, 0xBF, 0x06, 0x8B, 0x62, 0xB3, 0x25, 0xE2, 0x98, 0x22, 0x88, 0x91, 0x10, + 0x7E, 0x6E, 0x48, 0xC3, 0xA3, 0xB6, 0x1E, 0x42, 0x3A, 0x6B, 0x28, 0x54, 0xFA, 0x85, 0x3D, 0xBA, + 0x2B, 0x79, 0x0A, 0x15, 0x9B, 0x9F, 0x5E, 0xCA, 0x4E, 0xD4, 0xAC, 0xE5, 0xF3, 0x73, 0xA7, 0x57, + 0xAF, 0x58, 0xA8, 0x50, 0xF4, 0xEA, 0xD6, 0x74, 0x4F, 0xAE, 0xE9, 0xD5, 0xE7, 0xE6, 0xAD, 0xE8, + 0x2C, 0xD7, 0x75, 0x7A, 0xEB, 0x16, 0x0B, 0xF5, 0x59, 0xCB, 0x5F, 0xB0, 0x9C, 0xA9, 0x51, 0xA0, + 0x7F, 0x0C, 0xF6, 0x6F, 0x17, 0xC4, 0x49, 0xEC, 0xD8, 0x43, 0x1F, 0x2D, 0xA4, 0x76, 0x7B, 0xB7, + 0xCC, 0xBB, 0x3E, 0x5A, 0xFB, 0x60, 0xB1, 0x86, 0x3B, 0x52, 0xA1, 0x6C, 0xAA, 0x55, 0x29, 0x9D, + 0x97, 0xB2, 0x87, 0x90, 0x61, 0xBE, 0xDC, 0xFC, 0xBC, 0x95, 0xCF, 0xCD, 0x37, 0x3F, 0x5B, 0xD1, + 0x53, 0x39, 0x84, 0x3C, 0x41, 0xA2, 0x6D, 0x47, 0x14, 0x2A, 0x9E, 0x5D, 0x56, 0xF2, 0xD3, 0xAB, + 0x44, 0x11, 0x92, 0xD9, 0x23, 0x20, 0x2E, 0x89, 0xB4, 0x7C, 0xB8, 0x26, 0x77, 0x99, 0xE3, 0xA5, + 0x67, 0x4A, 0xED, 0xDE, 0xC5, 0x31, 0xFE, 0x18, 0x0D, 0x63, 0x8C, 0x80, 0xC0, 0xF7, 0x70, 0x07}; + +local MIXTABLE = { + 0x02, 0x03, 0x01, 0x01, + 0x01, 0x02, 0x03, 0x01, + 0x01, 0x01, 0x02, 0x03, + 0x03, 0x01, 0x01, 0x02}; + +local IMIXTABLE = { + 0x0E, 0x0B, 0x0D, 0x09, + 0x09, 0x0E, 0x0B, 0x0D, + 0x0D, 0x09, 0x0E, 0x0B, + 0x0B, 0x0D, 0x09, 0x0E}; + +local RCON = { +[0] = 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, +0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, +0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, +0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, +0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, +0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, +0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, +0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, +0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, +0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, +0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, +0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, +0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, +0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, +0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, +0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d}; + + +local GMUL = function(A, B) + if(A == 0x01) then return B; end + if(B == 0x01) then return A; end + if(A == 0x00) then return 0; end + if(B == 0x00) then return 0; end + + local LA = LTABLE[A]; + local LB = LTABLE[B]; + + local sum = LA + LB; + if (sum > 0xFF) then sum = sum - 0xFF; end + + return ETABLE[sum]; +end + +local byteSub = Array.substitute; + +local shiftRow = Array.permute; + +local mixCol = function(i, mix) + local out = {}; + + local a, b, c, d; + + a = GMUL(i[ 1], mix[ 1]); + b = GMUL(i[ 2], mix[ 2]); + c = GMUL(i[ 3], mix[ 3]); + d = GMUL(i[ 4], mix[ 4]); + out[ 1] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 1], mix[ 5]); + b = GMUL(i[ 2], mix[ 6]); + c = GMUL(i[ 3], mix[ 7]); + d = GMUL(i[ 4], mix[ 8]); + out[ 2] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 1], mix[ 9]); + b = GMUL(i[ 2], mix[10]); + c = GMUL(i[ 3], mix[11]); + d = GMUL(i[ 4], mix[12]); + out[ 3] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 1], mix[13]); + b = GMUL(i[ 2], mix[14]); + c = GMUL(i[ 3], mix[15]); + d = GMUL(i[ 4], mix[16]); + out[ 4] = XOR(XOR(a, b), XOR(c, d)); + + + a = GMUL(i[ 5], mix[ 1]); + b = GMUL(i[ 6], mix[ 2]); + c = GMUL(i[ 7], mix[ 3]); + d = GMUL(i[ 8], mix[ 4]); + out[ 5] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 5], mix[ 5]); + b = GMUL(i[ 6], mix[ 6]); + c = GMUL(i[ 7], mix[ 7]); + d = GMUL(i[ 8], mix[ 8]); + out[ 6] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 5], mix[ 9]); + b = GMUL(i[ 6], mix[10]); + c = GMUL(i[ 7], mix[11]); + d = GMUL(i[ 8], mix[12]); + out[ 7] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 5], mix[13]); + b = GMUL(i[ 6], mix[14]); + c = GMUL(i[ 7], mix[15]); + d = GMUL(i[ 8], mix[16]); + out[ 8] = XOR(XOR(a, b), XOR(c, d)); + + + a = GMUL(i[ 9], mix[ 1]); + b = GMUL(i[10], mix[ 2]); + c = GMUL(i[11], mix[ 3]); + d = GMUL(i[12], mix[ 4]); + out[ 9] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 9], mix[ 5]); + b = GMUL(i[10], mix[ 6]); + c = GMUL(i[11], mix[ 7]); + d = GMUL(i[12], mix[ 8]); + out[10] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 9], mix[ 9]); + b = GMUL(i[10], mix[10]); + c = GMUL(i[11], mix[11]); + d = GMUL(i[12], mix[12]); + out[11] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 9], mix[13]); + b = GMUL(i[10], mix[14]); + c = GMUL(i[11], mix[15]); + d = GMUL(i[12], mix[16]); + out[12] = XOR(XOR(a, b), XOR(c, d)); + + + a = GMUL(i[13], mix[ 1]); + b = GMUL(i[14], mix[ 2]); + c = GMUL(i[15], mix[ 3]); + d = GMUL(i[16], mix[ 4]); + out[13] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[13], mix[ 5]); + b = GMUL(i[14], mix[ 6]); + c = GMUL(i[15], mix[ 7]); + d = GMUL(i[16], mix[ 8]); + out[14] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[13], mix[ 9]); + b = GMUL(i[14], mix[10]); + c = GMUL(i[15], mix[11]); + d = GMUL(i[16], mix[12]); + out[15] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[13], mix[13]); + b = GMUL(i[14], mix[14]); + c = GMUL(i[15], mix[15]); + d = GMUL(i[16], mix[16]); + out[16] = XOR(XOR(a, b), XOR(c, d)); + + return out; +end + +local keyRound = function(key, round) + local out = {}; + + out[ 1] = XOR(key[ 1], XOR(SBOX[key[14]], RCON[round])); + out[ 2] = XOR(key[ 2], SBOX[key[15]]); + out[ 3] = XOR(key[ 3], SBOX[key[16]]); + out[ 4] = XOR(key[ 4], SBOX[key[13]]); + + out[ 5] = XOR(out[ 1], key[ 5]); + out[ 6] = XOR(out[ 2], key[ 6]); + out[ 7] = XOR(out[ 3], key[ 7]); + out[ 8] = XOR(out[ 4], key[ 8]); + + out[ 9] = XOR(out[ 5], key[ 9]); + out[10] = XOR(out[ 6], key[10]); + out[11] = XOR(out[ 7], key[11]); + out[12] = XOR(out[ 8], key[12]); + + out[13] = XOR(out[ 9], key[13]); + out[14] = XOR(out[10], key[14]); + out[15] = XOR(out[11], key[15]); + out[16] = XOR(out[12], key[16]); + + return out; +end + +local keyExpand = function(key) + local keys = {}; + + local temp = key; + + keys[1] = temp; + + for i = 1, 10 do + temp = keyRound(temp, i); + keys[i + 1] = temp; + end + + return keys; + +end + +local addKey = Array.XOR; + + + +local AES = {}; + +AES.blockSize = 16; + +AES.encrypt = function(key, block) + + local keySchedule = keyExpand(key); + + --round 0 + block = addKey(block, keySchedule[1]); + + --round 1 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[2]); + + --round 2 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[3]); + + --round 3 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[4]); + + --round 4 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[5]); + + --round 5 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[6]); + + --round 6 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[7]); + + --round 7 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[8]); + + --round 8 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[9]); + + --round 9 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[10]); + + --round 10 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = addKey(block, keySchedule[11]); + + return block; + +end + +AES.decrypt = function(key, block) + + local keySchedule = keyExpand(key); + + --round 0 + block = addKey(block, keySchedule[11]); + + --round 1 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[10]); + block = mixCol(block, IMIXTABLE); + + --round 2 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[9]); + block = mixCol(block, IMIXTABLE); + + --round 3 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[8]); + block = mixCol(block, IMIXTABLE); + + --round 4 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[7]); + block = mixCol(block, IMIXTABLE); + + --round 5 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[6]); + block = mixCol(block, IMIXTABLE); + + --round 6 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[5]); + block = mixCol(block, IMIXTABLE); + + --round 7 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[4]); + block = mixCol(block, IMIXTABLE); + + --round 8 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[3]); + block = mixCol(block, IMIXTABLE); + + --round 9 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[2]); + block = mixCol(block, IMIXTABLE); + + --round 10 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[1]); + + return block; +end + +return AES; \ No newline at end of file diff --git a/contract/src/crypto/cipher/aes192.lua b/contract/src/crypto/cipher/aes192.lua new file mode 100644 index 00000000..67eb6031 --- /dev/null +++ b/contract/src/crypto/cipher/aes192.lua @@ -0,0 +1,462 @@ + +local Array = require(".crypto.util.array"); +local Bit = require(".crypto.util.bit"); + +local XOR = Bit.bxor; + +local SBOX = { + [0] = 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16}; + +local ISBOX = { + [0] = 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D}; + +local ROW_SHIFT = { 1, 6, 11, 16, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, }; +local IROW_SHIFT = { 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3, 16, 13, 10, 7, 4, }; + +local ETABLE = { + [0] = 0x01, 0x03, 0x05, 0x0F, 0x11, 0x33, 0x55, 0xFF, 0x1A, 0x2E, 0x72, 0x96, 0xA1, 0xF8, 0x13, 0x35, + 0x5F, 0xE1, 0x38, 0x48, 0xD8, 0x73, 0x95, 0xA4, 0xF7, 0x02, 0x06, 0x0A, 0x1E, 0x22, 0x66, 0xAA, + 0xE5, 0x34, 0x5C, 0xE4, 0x37, 0x59, 0xEB, 0x26, 0x6A, 0xBE, 0xD9, 0x70, 0x90, 0xAB, 0xE6, 0x31, + 0x53, 0xF5, 0x04, 0x0C, 0x14, 0x3C, 0x44, 0xCC, 0x4F, 0xD1, 0x68, 0xB8, 0xD3, 0x6E, 0xB2, 0xCD, + 0x4C, 0xD4, 0x67, 0xA9, 0xE0, 0x3B, 0x4D, 0xD7, 0x62, 0xA6, 0xF1, 0x08, 0x18, 0x28, 0x78, 0x88, + 0x83, 0x9E, 0xB9, 0xD0, 0x6B, 0xBD, 0xDC, 0x7F, 0x81, 0x98, 0xB3, 0xCE, 0x49, 0xDB, 0x76, 0x9A, + 0xB5, 0xC4, 0x57, 0xF9, 0x10, 0x30, 0x50, 0xF0, 0x0B, 0x1D, 0x27, 0x69, 0xBB, 0xD6, 0x61, 0xA3, + 0xFE, 0x19, 0x2B, 0x7D, 0x87, 0x92, 0xAD, 0xEC, 0x2F, 0x71, 0x93, 0xAE, 0xE9, 0x20, 0x60, 0xA0, + 0xFB, 0x16, 0x3A, 0x4E, 0xD2, 0x6D, 0xB7, 0xC2, 0x5D, 0xE7, 0x32, 0x56, 0xFA, 0x15, 0x3F, 0x41, + 0xC3, 0x5E, 0xE2, 0x3D, 0x47, 0xC9, 0x40, 0xC0, 0x5B, 0xED, 0x2C, 0x74, 0x9C, 0xBF, 0xDA, 0x75, + 0x9F, 0xBA, 0xD5, 0x64, 0xAC, 0xEF, 0x2A, 0x7E, 0x82, 0x9D, 0xBC, 0xDF, 0x7A, 0x8E, 0x89, 0x80, + 0x9B, 0xB6, 0xC1, 0x58, 0xE8, 0x23, 0x65, 0xAF, 0xEA, 0x25, 0x6F, 0xB1, 0xC8, 0x43, 0xC5, 0x54, + 0xFC, 0x1F, 0x21, 0x63, 0xA5, 0xF4, 0x07, 0x09, 0x1B, 0x2D, 0x77, 0x99, 0xB0, 0xCB, 0x46, 0xCA, + 0x45, 0xCF, 0x4A, 0xDE, 0x79, 0x8B, 0x86, 0x91, 0xA8, 0xE3, 0x3E, 0x42, 0xC6, 0x51, 0xF3, 0x0E, + 0x12, 0x36, 0x5A, 0xEE, 0x29, 0x7B, 0x8D, 0x8C, 0x8F, 0x8A, 0x85, 0x94, 0xA7, 0xF2, 0x0D, 0x17, + 0x39, 0x4B, 0xDD, 0x7C, 0x84, 0x97, 0xA2, 0xFD, 0x1C, 0x24, 0x6C, 0xB4, 0xC7, 0x52, 0xF6, 0x01}; + +local LTABLE = { + [0] = 0x00, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1A, 0xC6, 0x4B, 0xC7, 0x1B, 0x68, 0x33, 0xEE, 0xDF, 0x03, + 0x64, 0x04, 0xE0, 0x0E, 0x34, 0x8D, 0x81, 0xEF, 0x4C, 0x71, 0x08, 0xC8, 0xF8, 0x69, 0x1C, 0xC1, + 0x7D, 0xC2, 0x1D, 0xB5, 0xF9, 0xB9, 0x27, 0x6A, 0x4D, 0xE4, 0xA6, 0x72, 0x9A, 0xC9, 0x09, 0x78, + 0x65, 0x2F, 0x8A, 0x05, 0x21, 0x0F, 0xE1, 0x24, 0x12, 0xF0, 0x82, 0x45, 0x35, 0x93, 0xDA, 0x8E, + 0x96, 0x8F, 0xDB, 0xBD, 0x36, 0xD0, 0xCE, 0x94, 0x13, 0x5C, 0xD2, 0xF1, 0x40, 0x46, 0x83, 0x38, + 0x66, 0xDD, 0xFD, 0x30, 0xBF, 0x06, 0x8B, 0x62, 0xB3, 0x25, 0xE2, 0x98, 0x22, 0x88, 0x91, 0x10, + 0x7E, 0x6E, 0x48, 0xC3, 0xA3, 0xB6, 0x1E, 0x42, 0x3A, 0x6B, 0x28, 0x54, 0xFA, 0x85, 0x3D, 0xBA, + 0x2B, 0x79, 0x0A, 0x15, 0x9B, 0x9F, 0x5E, 0xCA, 0x4E, 0xD4, 0xAC, 0xE5, 0xF3, 0x73, 0xA7, 0x57, + 0xAF, 0x58, 0xA8, 0x50, 0xF4, 0xEA, 0xD6, 0x74, 0x4F, 0xAE, 0xE9, 0xD5, 0xE7, 0xE6, 0xAD, 0xE8, + 0x2C, 0xD7, 0x75, 0x7A, 0xEB, 0x16, 0x0B, 0xF5, 0x59, 0xCB, 0x5F, 0xB0, 0x9C, 0xA9, 0x51, 0xA0, + 0x7F, 0x0C, 0xF6, 0x6F, 0x17, 0xC4, 0x49, 0xEC, 0xD8, 0x43, 0x1F, 0x2D, 0xA4, 0x76, 0x7B, 0xB7, + 0xCC, 0xBB, 0x3E, 0x5A, 0xFB, 0x60, 0xB1, 0x86, 0x3B, 0x52, 0xA1, 0x6C, 0xAA, 0x55, 0x29, 0x9D, + 0x97, 0xB2, 0x87, 0x90, 0x61, 0xBE, 0xDC, 0xFC, 0xBC, 0x95, 0xCF, 0xCD, 0x37, 0x3F, 0x5B, 0xD1, + 0x53, 0x39, 0x84, 0x3C, 0x41, 0xA2, 0x6D, 0x47, 0x14, 0x2A, 0x9E, 0x5D, 0x56, 0xF2, 0xD3, 0xAB, + 0x44, 0x11, 0x92, 0xD9, 0x23, 0x20, 0x2E, 0x89, 0xB4, 0x7C, 0xB8, 0x26, 0x77, 0x99, 0xE3, 0xA5, + 0x67, 0x4A, 0xED, 0xDE, 0xC5, 0x31, 0xFE, 0x18, 0x0D, 0x63, 0x8C, 0x80, 0xC0, 0xF7, 0x70, 0x07}; + +local MIXTABLE = { + 0x02, 0x03, 0x01, 0x01, + 0x01, 0x02, 0x03, 0x01, + 0x01, 0x01, 0x02, 0x03, + 0x03, 0x01, 0x01, 0x02}; + +local IMIXTABLE = { + 0x0E, 0x0B, 0x0D, 0x09, + 0x09, 0x0E, 0x0B, 0x0D, + 0x0D, 0x09, 0x0E, 0x0B, + 0x0B, 0x0D, 0x09, 0x0E}; + +local RCON = { +[0] = 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, +0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, +0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, +0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, +0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, +0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, +0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, +0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, +0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, +0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, +0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, +0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, +0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, +0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, +0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, +0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d}; + + +local GMUL = function(A, B) + if(A == 0x01) then return B; end + if(B == 0x01) then return A; end + if(A == 0x00) then return 0; end + if(B == 0x00) then return 0; end + + local LA = LTABLE[A]; + local LB = LTABLE[B]; + + local sum = LA + LB; + if (sum > 0xFF) then sum = sum - 0xFF; end + + return ETABLE[sum]; +end + +local byteSub = Array.substitute; + +local shiftRow = Array.permute; + +local mixCol = function(i, mix) + local out = {}; + + local a, b, c, d; + + a = GMUL(i[ 1], mix[ 1]); + b = GMUL(i[ 2], mix[ 2]); + c = GMUL(i[ 3], mix[ 3]); + d = GMUL(i[ 4], mix[ 4]); + out[ 1] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 1], mix[ 5]); + b = GMUL(i[ 2], mix[ 6]); + c = GMUL(i[ 3], mix[ 7]); + d = GMUL(i[ 4], mix[ 8]); + out[ 2] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 1], mix[ 9]); + b = GMUL(i[ 2], mix[10]); + c = GMUL(i[ 3], mix[11]); + d = GMUL(i[ 4], mix[12]); + out[ 3] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 1], mix[13]); + b = GMUL(i[ 2], mix[14]); + c = GMUL(i[ 3], mix[15]); + d = GMUL(i[ 4], mix[16]); + out[ 4] = XOR(XOR(a, b), XOR(c, d)); + + + a = GMUL(i[ 5], mix[ 1]); + b = GMUL(i[ 6], mix[ 2]); + c = GMUL(i[ 7], mix[ 3]); + d = GMUL(i[ 8], mix[ 4]); + out[ 5] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 5], mix[ 5]); + b = GMUL(i[ 6], mix[ 6]); + c = GMUL(i[ 7], mix[ 7]); + d = GMUL(i[ 8], mix[ 8]); + out[ 6] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 5], mix[ 9]); + b = GMUL(i[ 6], mix[10]); + c = GMUL(i[ 7], mix[11]); + d = GMUL(i[ 8], mix[12]); + out[ 7] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 5], mix[13]); + b = GMUL(i[ 6], mix[14]); + c = GMUL(i[ 7], mix[15]); + d = GMUL(i[ 8], mix[16]); + out[ 8] = XOR(XOR(a, b), XOR(c, d)); + + + a = GMUL(i[ 9], mix[ 1]); + b = GMUL(i[10], mix[ 2]); + c = GMUL(i[11], mix[ 3]); + d = GMUL(i[12], mix[ 4]); + out[ 9] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 9], mix[ 5]); + b = GMUL(i[10], mix[ 6]); + c = GMUL(i[11], mix[ 7]); + d = GMUL(i[12], mix[ 8]); + out[10] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 9], mix[ 9]); + b = GMUL(i[10], mix[10]); + c = GMUL(i[11], mix[11]); + d = GMUL(i[12], mix[12]); + out[11] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 9], mix[13]); + b = GMUL(i[10], mix[14]); + c = GMUL(i[11], mix[15]); + d = GMUL(i[12], mix[16]); + out[12] = XOR(XOR(a, b), XOR(c, d)); + + + a = GMUL(i[13], mix[ 1]); + b = GMUL(i[14], mix[ 2]); + c = GMUL(i[15], mix[ 3]); + d = GMUL(i[16], mix[ 4]); + out[13] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[13], mix[ 5]); + b = GMUL(i[14], mix[ 6]); + c = GMUL(i[15], mix[ 7]); + d = GMUL(i[16], mix[ 8]); + out[14] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[13], mix[ 9]); + b = GMUL(i[14], mix[10]); + c = GMUL(i[15], mix[11]); + d = GMUL(i[16], mix[12]); + out[15] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[13], mix[13]); + b = GMUL(i[14], mix[14]); + c = GMUL(i[15], mix[15]); + d = GMUL(i[16], mix[16]); + out[16] = XOR(XOR(a, b), XOR(c, d)); + + return out; +end + +local keyRound = function(key, round) + local i = (round - 1) * 24; + local out = key; + + out[25 + i] = XOR(key[ 1 + i], XOR(SBOX[key[22 + i]], RCON[round])); + out[26 + i] = XOR(key[ 2 + i], SBOX[key[23 + i]]); + out[27 + i] = XOR(key[ 3 + i], SBOX[key[24 + i]]); + out[28 + i] = XOR(key[ 4 + i], SBOX[key[21 + i]]); + + out[29 + i] = XOR(out[25 + i], key[ 5 + i]); + out[30 + i] = XOR(out[26 + i], key[ 6 + i]); + out[31 + i] = XOR(out[27 + i], key[ 7 + i]); + out[32 + i] = XOR(out[28 + i], key[ 8 + i]); + + out[33 + i] = XOR(out[29 + i], key[ 9 + i]); + out[34 + i] = XOR(out[30 + i], key[10 + i]); + out[35 + i] = XOR(out[31 + i], key[11 + i]); + out[36 + i] = XOR(out[32 + i], key[12 + i]); + + out[37 + i] = XOR(out[33 + i], key[13 + i]); + out[38 + i] = XOR(out[34 + i], key[14 + i]); + out[39 + i] = XOR(out[35 + i], key[15 + i]); + out[40 + i] = XOR(out[36 + i], key[16 + i]); + + out[41 + i] = XOR(out[37 + i], key[17 + i]); + out[42 + i] = XOR(out[38 + i], key[18 + i]); + out[43 + i] = XOR(out[39 + i], key[19 + i]); + out[44 + i] = XOR(out[40 + i], key[20 + i]); + + out[45 + i] = XOR(out[41 + i], key[21 + i]); + out[46 + i] = XOR(out[42 + i], key[22 + i]); + out[47 + i] = XOR(out[43 + i], key[23 + i]); + out[48 + i] = XOR(out[44 + i], key[24 + i]); + + return out; +end + +local keyExpand = function(key) + local bytes = Array.copy(key); + + for i = 1, 8 do + keyRound(bytes, i); + end + + local keys = {}; + + keys[ 1] = Array.slice(bytes, 1, 16); + keys[ 2] = Array.slice(bytes, 17, 32); + keys[ 3] = Array.slice(bytes, 33, 48); + keys[ 4] = Array.slice(bytes, 49, 64); + keys[ 5] = Array.slice(bytes, 65, 80); + keys[ 6] = Array.slice(bytes, 81, 96); + keys[ 7] = Array.slice(bytes, 97, 112); + keys[ 8] = Array.slice(bytes, 113, 128); + keys[ 9] = Array.slice(bytes, 129, 144); + keys[10] = Array.slice(bytes, 145, 160); + keys[11] = Array.slice(bytes, 161, 176); + keys[12] = Array.slice(bytes, 177, 192); + keys[13] = Array.slice(bytes, 193, 208); + + return keys; + +end + +local addKey = Array.XOR; + + + +local AES = {}; + +AES.blockSize = 16; + +AES.encrypt = function(key, block) + + local keySchedule = keyExpand(key); + + --round 0 + block = addKey(block, keySchedule[1]); + + --round 1 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[2]); + + --round 2 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[3]); + + --round 3 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[4]); + + --round 4 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[5]); + + --round 5 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[6]); + + --round 6 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[7]); + + --round 7 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[8]); + + --round 8 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[9]); + + --round 9 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[10]); + + --round 10 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[11]); + + --round 11 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[12]); + + --round 12 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = addKey(block, keySchedule[13]); + + return block; + +end + +AES.decrypt = function(key, block) + + local keySchedule = keyExpand(key); + + --round 0 + block = addKey(block, keySchedule[13]); + + --round 1 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[12]); + block = mixCol(block, IMIXTABLE); + + --round 2 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[11]); + block = mixCol(block, IMIXTABLE); + + --round 3 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[10]); + block = mixCol(block, IMIXTABLE); + + --round 4 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[9]); + block = mixCol(block, IMIXTABLE); + + --round 5 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[8]); + block = mixCol(block, IMIXTABLE); + + --round 6 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[7]); + block = mixCol(block, IMIXTABLE); + + --round 7 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[6]); + block = mixCol(block, IMIXTABLE); + + --round 8 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[5]); + block = mixCol(block, IMIXTABLE); + + --round 9 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[4]); + block = mixCol(block, IMIXTABLE); + + --round 10 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[3]); + block = mixCol(block, IMIXTABLE); + + --round 11 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[2]); + block = mixCol(block, IMIXTABLE); + + --round 12 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[1]); + + return block; +end + +return AES; \ No newline at end of file diff --git a/contract/src/crypto/cipher/aes256.lua b/contract/src/crypto/cipher/aes256.lua new file mode 100644 index 00000000..bc79e77d --- /dev/null +++ b/contract/src/crypto/cipher/aes256.lua @@ -0,0 +1,498 @@ +local Array = require(".crypto.util.array"); +local Bit = require(".crypto.util.bit"); + +local XOR = Bit.bxor; + +local SBOX = { + [0] = 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76, + 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, + 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75, + 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, + 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8, + 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, + 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB, + 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, + 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A, + 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, + 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16}; + +local ISBOX = { + [0] = 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB, + 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, + 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25, + 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, + 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06, + 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, + 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E, + 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, + 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F, + 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, + 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D}; + +local ROW_SHIFT = { 1, 6, 11, 16, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, }; +local IROW_SHIFT = { 1, 14, 11, 8, 5, 2, 15, 12, 9, 6, 3, 16, 13, 10, 7, 4, }; + +local ETABLE = { + [0] = 0x01, 0x03, 0x05, 0x0F, 0x11, 0x33, 0x55, 0xFF, 0x1A, 0x2E, 0x72, 0x96, 0xA1, 0xF8, 0x13, 0x35, + 0x5F, 0xE1, 0x38, 0x48, 0xD8, 0x73, 0x95, 0xA4, 0xF7, 0x02, 0x06, 0x0A, 0x1E, 0x22, 0x66, 0xAA, + 0xE5, 0x34, 0x5C, 0xE4, 0x37, 0x59, 0xEB, 0x26, 0x6A, 0xBE, 0xD9, 0x70, 0x90, 0xAB, 0xE6, 0x31, + 0x53, 0xF5, 0x04, 0x0C, 0x14, 0x3C, 0x44, 0xCC, 0x4F, 0xD1, 0x68, 0xB8, 0xD3, 0x6E, 0xB2, 0xCD, + 0x4C, 0xD4, 0x67, 0xA9, 0xE0, 0x3B, 0x4D, 0xD7, 0x62, 0xA6, 0xF1, 0x08, 0x18, 0x28, 0x78, 0x88, + 0x83, 0x9E, 0xB9, 0xD0, 0x6B, 0xBD, 0xDC, 0x7F, 0x81, 0x98, 0xB3, 0xCE, 0x49, 0xDB, 0x76, 0x9A, + 0xB5, 0xC4, 0x57, 0xF9, 0x10, 0x30, 0x50, 0xF0, 0x0B, 0x1D, 0x27, 0x69, 0xBB, 0xD6, 0x61, 0xA3, + 0xFE, 0x19, 0x2B, 0x7D, 0x87, 0x92, 0xAD, 0xEC, 0x2F, 0x71, 0x93, 0xAE, 0xE9, 0x20, 0x60, 0xA0, + 0xFB, 0x16, 0x3A, 0x4E, 0xD2, 0x6D, 0xB7, 0xC2, 0x5D, 0xE7, 0x32, 0x56, 0xFA, 0x15, 0x3F, 0x41, + 0xC3, 0x5E, 0xE2, 0x3D, 0x47, 0xC9, 0x40, 0xC0, 0x5B, 0xED, 0x2C, 0x74, 0x9C, 0xBF, 0xDA, 0x75, + 0x9F, 0xBA, 0xD5, 0x64, 0xAC, 0xEF, 0x2A, 0x7E, 0x82, 0x9D, 0xBC, 0xDF, 0x7A, 0x8E, 0x89, 0x80, + 0x9B, 0xB6, 0xC1, 0x58, 0xE8, 0x23, 0x65, 0xAF, 0xEA, 0x25, 0x6F, 0xB1, 0xC8, 0x43, 0xC5, 0x54, + 0xFC, 0x1F, 0x21, 0x63, 0xA5, 0xF4, 0x07, 0x09, 0x1B, 0x2D, 0x77, 0x99, 0xB0, 0xCB, 0x46, 0xCA, + 0x45, 0xCF, 0x4A, 0xDE, 0x79, 0x8B, 0x86, 0x91, 0xA8, 0xE3, 0x3E, 0x42, 0xC6, 0x51, 0xF3, 0x0E, + 0x12, 0x36, 0x5A, 0xEE, 0x29, 0x7B, 0x8D, 0x8C, 0x8F, 0x8A, 0x85, 0x94, 0xA7, 0xF2, 0x0D, 0x17, + 0x39, 0x4B, 0xDD, 0x7C, 0x84, 0x97, 0xA2, 0xFD, 0x1C, 0x24, 0x6C, 0xB4, 0xC7, 0x52, 0xF6, 0x01}; + +local LTABLE = { + [0] = 0x00, 0x00, 0x19, 0x01, 0x32, 0x02, 0x1A, 0xC6, 0x4B, 0xC7, 0x1B, 0x68, 0x33, 0xEE, 0xDF, 0x03, + 0x64, 0x04, 0xE0, 0x0E, 0x34, 0x8D, 0x81, 0xEF, 0x4C, 0x71, 0x08, 0xC8, 0xF8, 0x69, 0x1C, 0xC1, + 0x7D, 0xC2, 0x1D, 0xB5, 0xF9, 0xB9, 0x27, 0x6A, 0x4D, 0xE4, 0xA6, 0x72, 0x9A, 0xC9, 0x09, 0x78, + 0x65, 0x2F, 0x8A, 0x05, 0x21, 0x0F, 0xE1, 0x24, 0x12, 0xF0, 0x82, 0x45, 0x35, 0x93, 0xDA, 0x8E, + 0x96, 0x8F, 0xDB, 0xBD, 0x36, 0xD0, 0xCE, 0x94, 0x13, 0x5C, 0xD2, 0xF1, 0x40, 0x46, 0x83, 0x38, + 0x66, 0xDD, 0xFD, 0x30, 0xBF, 0x06, 0x8B, 0x62, 0xB3, 0x25, 0xE2, 0x98, 0x22, 0x88, 0x91, 0x10, + 0x7E, 0x6E, 0x48, 0xC3, 0xA3, 0xB6, 0x1E, 0x42, 0x3A, 0x6B, 0x28, 0x54, 0xFA, 0x85, 0x3D, 0xBA, + 0x2B, 0x79, 0x0A, 0x15, 0x9B, 0x9F, 0x5E, 0xCA, 0x4E, 0xD4, 0xAC, 0xE5, 0xF3, 0x73, 0xA7, 0x57, + 0xAF, 0x58, 0xA8, 0x50, 0xF4, 0xEA, 0xD6, 0x74, 0x4F, 0xAE, 0xE9, 0xD5, 0xE7, 0xE6, 0xAD, 0xE8, + 0x2C, 0xD7, 0x75, 0x7A, 0xEB, 0x16, 0x0B, 0xF5, 0x59, 0xCB, 0x5F, 0xB0, 0x9C, 0xA9, 0x51, 0xA0, + 0x7F, 0x0C, 0xF6, 0x6F, 0x17, 0xC4, 0x49, 0xEC, 0xD8, 0x43, 0x1F, 0x2D, 0xA4, 0x76, 0x7B, 0xB7, + 0xCC, 0xBB, 0x3E, 0x5A, 0xFB, 0x60, 0xB1, 0x86, 0x3B, 0x52, 0xA1, 0x6C, 0xAA, 0x55, 0x29, 0x9D, + 0x97, 0xB2, 0x87, 0x90, 0x61, 0xBE, 0xDC, 0xFC, 0xBC, 0x95, 0xCF, 0xCD, 0x37, 0x3F, 0x5B, 0xD1, + 0x53, 0x39, 0x84, 0x3C, 0x41, 0xA2, 0x6D, 0x47, 0x14, 0x2A, 0x9E, 0x5D, 0x56, 0xF2, 0xD3, 0xAB, + 0x44, 0x11, 0x92, 0xD9, 0x23, 0x20, 0x2E, 0x89, 0xB4, 0x7C, 0xB8, 0x26, 0x77, 0x99, 0xE3, 0xA5, + 0x67, 0x4A, 0xED, 0xDE, 0xC5, 0x31, 0xFE, 0x18, 0x0D, 0x63, 0x8C, 0x80, 0xC0, 0xF7, 0x70, 0x07}; + +local MIXTABLE = { + 0x02, 0x03, 0x01, 0x01, + 0x01, 0x02, 0x03, 0x01, + 0x01, 0x01, 0x02, 0x03, + 0x03, 0x01, 0x01, 0x02}; + +local IMIXTABLE = { + 0x0E, 0x0B, 0x0D, 0x09, + 0x09, 0x0E, 0x0B, 0x0D, + 0x0D, 0x09, 0x0E, 0x0B, + 0x0B, 0x0D, 0x09, 0x0E}; + +local RCON = { +[0] = 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, +0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, +0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, +0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, +0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, +0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, +0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, +0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, +0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, +0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, +0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, +0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, 0x61, 0xc2, 0x9f, +0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d, 0x01, 0x02, 0x04, +0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, +0xc6, 0x97, 0x35, 0x6a, 0xd4, 0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91, 0x39, 0x72, 0xe4, 0xd3, 0xbd, +0x61, 0xc2, 0x9f, 0x25, 0x4a, 0x94, 0x33, 0x66, 0xcc, 0x83, 0x1d, 0x3a, 0x74, 0xe8, 0xcb, 0x8d}; + + +local GMUL = function(A, B) + if(A == 0x01) then return B; end + if(B == 0x01) then return A; end + if(A == 0x00) then return 0; end + if(B == 0x00) then return 0; end + + local LA = LTABLE[A]; + local LB = LTABLE[B]; + + local sum = LA + LB; + if (sum > 0xFF) then sum = sum - 0xFF; end + + return ETABLE[sum]; +end + +local byteSub = Array.substitute; + +local shiftRow = Array.permute; + +local mixCol = function(i, mix) + local out = {}; + + local a, b, c, d; + + a = GMUL(i[ 1], mix[ 1]); + b = GMUL(i[ 2], mix[ 2]); + c = GMUL(i[ 3], mix[ 3]); + d = GMUL(i[ 4], mix[ 4]); + out[ 1] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 1], mix[ 5]); + b = GMUL(i[ 2], mix[ 6]); + c = GMUL(i[ 3], mix[ 7]); + d = GMUL(i[ 4], mix[ 8]); + out[ 2] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 1], mix[ 9]); + b = GMUL(i[ 2], mix[10]); + c = GMUL(i[ 3], mix[11]); + d = GMUL(i[ 4], mix[12]); + out[ 3] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 1], mix[13]); + b = GMUL(i[ 2], mix[14]); + c = GMUL(i[ 3], mix[15]); + d = GMUL(i[ 4], mix[16]); + out[ 4] = XOR(XOR(a, b), XOR(c, d)); + + + a = GMUL(i[ 5], mix[ 1]); + b = GMUL(i[ 6], mix[ 2]); + c = GMUL(i[ 7], mix[ 3]); + d = GMUL(i[ 8], mix[ 4]); + out[ 5] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 5], mix[ 5]); + b = GMUL(i[ 6], mix[ 6]); + c = GMUL(i[ 7], mix[ 7]); + d = GMUL(i[ 8], mix[ 8]); + out[ 6] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 5], mix[ 9]); + b = GMUL(i[ 6], mix[10]); + c = GMUL(i[ 7], mix[11]); + d = GMUL(i[ 8], mix[12]); + out[ 7] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 5], mix[13]); + b = GMUL(i[ 6], mix[14]); + c = GMUL(i[ 7], mix[15]); + d = GMUL(i[ 8], mix[16]); + out[ 8] = XOR(XOR(a, b), XOR(c, d)); + + + a = GMUL(i[ 9], mix[ 1]); + b = GMUL(i[10], mix[ 2]); + c = GMUL(i[11], mix[ 3]); + d = GMUL(i[12], mix[ 4]); + out[ 9] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 9], mix[ 5]); + b = GMUL(i[10], mix[ 6]); + c = GMUL(i[11], mix[ 7]); + d = GMUL(i[12], mix[ 8]); + out[10] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 9], mix[ 9]); + b = GMUL(i[10], mix[10]); + c = GMUL(i[11], mix[11]); + d = GMUL(i[12], mix[12]); + out[11] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[ 9], mix[13]); + b = GMUL(i[10], mix[14]); + c = GMUL(i[11], mix[15]); + d = GMUL(i[12], mix[16]); + out[12] = XOR(XOR(a, b), XOR(c, d)); + + + a = GMUL(i[13], mix[ 1]); + b = GMUL(i[14], mix[ 2]); + c = GMUL(i[15], mix[ 3]); + d = GMUL(i[16], mix[ 4]); + out[13] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[13], mix[ 5]); + b = GMUL(i[14], mix[ 6]); + c = GMUL(i[15], mix[ 7]); + d = GMUL(i[16], mix[ 8]); + out[14] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[13], mix[ 9]); + b = GMUL(i[14], mix[10]); + c = GMUL(i[15], mix[11]); + d = GMUL(i[16], mix[12]); + out[15] = XOR(XOR(a, b), XOR(c, d)); + a = GMUL(i[13], mix[13]); + b = GMUL(i[14], mix[14]); + c = GMUL(i[15], mix[15]); + d = GMUL(i[16], mix[16]); + out[16] = XOR(XOR(a, b), XOR(c, d)); + + return out; +end + +local keyRound = function(key, round) + local i = (round - 1) * 32; + local out = key; + + out[33 + i] = XOR(key[ 1 + i], XOR(SBOX[key[30 + i]], RCON[round])); + out[34 + i] = XOR(key[ 2 + i], SBOX[key[31 + i]]); + out[35 + i] = XOR(key[ 3 + i], SBOX[key[32 + i]]); + out[36 + i] = XOR(key[ 4 + i], SBOX[key[29 + i]]); + + out[37 + i] = XOR(out[33 + i], key[ 5 + i]); + out[38 + i] = XOR(out[34 + i], key[ 6 + i]); + out[39 + i] = XOR(out[35 + i], key[ 7 + i]); + out[40 + i] = XOR(out[36 + i], key[ 8 + i]); + + out[41 + i] = XOR(out[37 + i], key[ 9 + i]); + out[42 + i] = XOR(out[38 + i], key[10 + i]); + out[43 + i] = XOR(out[39 + i], key[11 + i]); + out[44 + i] = XOR(out[40 + i], key[12 + i]); + + out[45 + i] = XOR(out[41 + i], key[13 + i]); + out[46 + i] = XOR(out[42 + i], key[14 + i]); + out[47 + i] = XOR(out[43 + i], key[15 + i]); + out[48 + i] = XOR(out[44 + i], key[16 + i]); + + + out[49 + i] = XOR(SBOX[out[45 + i]], key[17 + i]); + out[50 + i] = XOR(SBOX[out[46 + i]], key[18 + i]); + out[51 + i] = XOR(SBOX[out[47 + i]], key[19 + i]); + out[52 + i] = XOR(SBOX[out[48 + i]], key[20 + i]); + + out[53 + i] = XOR(out[49 + i], key[21 + i]); + out[54 + i] = XOR(out[50 + i], key[22 + i]); + out[55 + i] = XOR(out[51 + i], key[23 + i]); + out[56 + i] = XOR(out[52 + i], key[24 + i]); + + out[57 + i] = XOR(out[53 + i], key[25 + i]); + out[58 + i] = XOR(out[54 + i], key[26 + i]); + out[59 + i] = XOR(out[55 + i], key[27 + i]); + out[60 + i] = XOR(out[56 + i], key[28 + i]); + + out[61 + i] = XOR(out[57 + i], key[29 + i]); + out[62 + i] = XOR(out[58 + i], key[30 + i]); + out[63 + i] = XOR(out[59 + i], key[31 + i]); + out[64 + i] = XOR(out[60 + i], key[32 + i]); + + return out; +end + +local keyExpand = function(key) + local bytes = Array.copy(key); + + for i = 1, 7 do + keyRound(bytes, i); + end + + local keys = {}; + + keys[ 1] = Array.slice(bytes, 1, 16); + keys[ 2] = Array.slice(bytes, 17, 32); + keys[ 3] = Array.slice(bytes, 33, 48); + keys[ 4] = Array.slice(bytes, 49, 64); + keys[ 5] = Array.slice(bytes, 65, 80); + keys[ 6] = Array.slice(bytes, 81, 96); + keys[ 7] = Array.slice(bytes, 97, 112); + keys[ 8] = Array.slice(bytes, 113, 128); + keys[ 9] = Array.slice(bytes, 129, 144); + keys[10] = Array.slice(bytes, 145, 160); + keys[11] = Array.slice(bytes, 161, 176); + keys[12] = Array.slice(bytes, 177, 192); + keys[13] = Array.slice(bytes, 193, 208); + keys[14] = Array.slice(bytes, 209, 224); + keys[15] = Array.slice(bytes, 225, 240); + + return keys; + +end + +local addKey = Array.XOR; + + + +local AES = {}; + +AES.blockSize = 16; + +AES.encrypt = function(key, block) + + local keySchedule = keyExpand(key); + + --round 0 + block = addKey(block, keySchedule[1]); + + --round 1 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[2]); + + --round 2 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[3]); + + --round 3 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[4]); + + --round 4 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[5]); + + --round 5 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[6]); + + --round 6 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[7]); + + --round 7 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[8]); + + --round 8 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[9]); + + --round 9 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[10]); + + --round 10 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[11]); + + --round 11 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[12]); + + --round 12 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[13]); + + --round 13 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, keySchedule[14]); + + --round 14 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = addKey(block, keySchedule[15]); + + return block; + +end + +AES.decrypt = function(key, block) + + local keySchedule = keyExpand(key); + + --round 0 + block = addKey(block, keySchedule[15]); + + --round 1 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[14]); + block = mixCol(block, IMIXTABLE); + + --round 2 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[13]); + block = mixCol(block, IMIXTABLE); + + --round 3 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[12]); + block = mixCol(block, IMIXTABLE); + + --round 4 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[11]); + block = mixCol(block, IMIXTABLE); + + --round 5 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[10]); + block = mixCol(block, IMIXTABLE); + + --round 6 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[9]); + block = mixCol(block, IMIXTABLE); + + --round 7 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[8]); + block = mixCol(block, IMIXTABLE); + + --round 8 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[7]); + block = mixCol(block, IMIXTABLE); + + --round 9 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[6]); + block = mixCol(block, IMIXTABLE); + + --round 10 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[5]); + block = mixCol(block, IMIXTABLE); + + --round 11 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[4]); + block = mixCol(block, IMIXTABLE); + + --round 12 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[3]); + block = mixCol(block, IMIXTABLE); + + --round 13 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[2]); + block = mixCol(block, IMIXTABLE); + + --round 14 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, keySchedule[1]); + + return block; +end + +return AES; \ No newline at end of file diff --git a/contract/src/crypto/cipher/init.lua b/contract/src/crypto/cipher/init.lua new file mode 100644 index 00000000..72e8e8a9 --- /dev/null +++ b/contract/src/crypto/cipher/init.lua @@ -0,0 +1,14 @@ +local issac = require(".crypto.cipher.issac") +local morus = require(".crypto.cipher.morus") +local aes = require(".crypto.cipher.aes") +local norx = require(".crypto.cipher.norx") + +local cipher = { + _version = "0.0.1", + issac = issac, + morus = morus, + aes = aes, + norx = norx +}; + +return cipher \ No newline at end of file diff --git a/contract/src/crypto/cipher/issac.lua b/contract/src/crypto/cipher/issac.lua new file mode 100644 index 00000000..4ec39fc3 --- /dev/null +++ b/contract/src/crypto/cipher/issac.lua @@ -0,0 +1,204 @@ +local Hex = require(".crypto.util.hex"); + +-- External Results +local randRsl = {}; +local randCnt = 0; + +-- Internal State +local mm = {}; +local aa,bb,cc = 0,0,0; + +-- Cap to maintain 32 bit maths +local cap = 0x100000000; + +-- CipherMode +local ENCRYPT = 1; +local DECRYPT = 2; + +local function isaac() + cc = ( cc + 1 ) % cap; -- cc just gets incremented once per 256 results + bb = ( bb + cc ) % cap; -- then combined with bb + + for i = 0,255 do + local x = mm[i]; + local y; + local imod = i % 4; + if imod == 0 then aa = aa ~ (aa << 13); + elseif imod == 1 then aa = aa ~ (aa >> 6); + elseif imod == 2 then aa = aa ~ (aa << 2); + elseif imod == 3 then aa = aa ~ (aa >> 16); + end + aa = ( mm[(i+128)%256] + aa ) % cap; + y = ( mm[(x>>2) % 256] + aa + bb ) % cap; + mm[i] = y; + bb = ( mm[(y>>10)%256] + x ) % cap; + randRsl[i] = bb; + end + + randCnt = 0; -- Prepare to use the first set of results. + +end + +local function mix(a) + a[0] = ( a[0] ~ ( a[1] << 11 ) ) % cap; a[3] = ( a[3] + a[0] ) % cap; a[1] = ( a[1] + a[2] ) % cap; + a[1] = ( a[1] ~ ( a[2] >> 2 ) ) % cap; a[4] = ( a[4] + a[1] ) % cap; a[2] = ( a[2] + a[3] ) % cap; + a[2] = ( a[2] ~ ( a[3] << 8 ) ) % cap; a[5] = ( a[5] + a[2] ) % cap; a[3] = ( a[3] + a[4] ) % cap; + a[3] = ( a[3] ~ ( a[4] >> 16 ) ) % cap; a[6] = ( a[6] + a[3] ) % cap; a[4] = ( a[4] + a[5] ) % cap; + a[4] = ( a[4] ~ ( a[5] << 10 ) ) % cap; a[7] = ( a[7] + a[4] ) % cap; a[5] = ( a[5] + a[6] ) % cap; + a[5] = ( a[5] ~ ( a[6] >> 4 ) ) % cap; a[0] = ( a[0] + a[5] ) % cap; a[6] = ( a[6] + a[7] ) % cap; + a[6] = ( a[6] ~ ( a[7] << 8 ) ) % cap; a[1] = ( a[1] + a[6] ) % cap; a[7] = ( a[7] + a[0] ) % cap; + a[7] = ( a[7] ~ ( a[0] >> 9 ) ) % cap; a[2] = ( a[2] + a[7] ) % cap; a[0] = ( a[0] + a[1] ) % cap; +end + +local function randInit(flag) + + -- The golden ratio in 32 bit + -- math.floor((((math.sqrt(5)+1)/2)%1)*2^32) == 2654435769 == 0x9e3779b9 + local a = { [0] = 0x9e3779b9, 0x9e3779b9, 0x9e3779b9, 0x9e3779b9, 0x9e3779b9, 0x9e3779b9, 0x9e3779b9, 0x9e3779b9, }; + + aa,bb,cc = 0,0,0; + + for i = 1,4 do mix(a) end -- Scramble it. + + for i = 0,255,8 do -- Fill in mm[] with messy stuff. + if flag then -- Use all the information in the seed. + for j = 0,7 do + a[j] = ( a[j] + randRsl[i+j] ) % cap; + end + end + mix(a); + for j = 0,7 do + mm[i+j] = a[j]; + end + end + + if flag then + -- Do a second pass to make all of the seed affect all of mm. + for i = 0,255,8 do + for j = 0,7 do + a[j] = ( a[j] + mm[i+j] ) % cap; + end + mix(a); + for j = 0,7 do + mm[i+j] = a[j]; + end + end + end + + isaac(); -- Fill in the first set of results. + randCnt = 0; -- Prepare to use the first set of results. + +end + +--- Seeds the ISAAC random number generator with the given seed. +--- @param seed string - The seed to use for the random number generator. +--- @param flag? boolean - Whether to use all the information in the seed. Defaults to true. +local function seedIsaac(seed, flag) + local seedLength = #seed; + for i = 0,255 do mm[i] = 0; end + for i = 0,255 do randRsl[i] = seed:byte(i+1,i+1) or 0; end + randInit(flag); +end + +--- Retrieves a random number from the ISAAC random number generator +--- @return number: The random number +local function getRandom() + local result = randRsl[randCnt]; + randCnt = randCnt + 1; + if randCnt > 255 then + isaac(); + randCnt = 0; + end + return result; +end + +--- Get a random 32-bit value within the specified range. +--- @param min? number (optional) - The minimum value of the range. Defaults to 0. +--- @param max? number (optional) - The maximum value of the range. Defaults to 2^31-1. +--- @param seed? string (optional) - The seed to use for the random number generator. +--- @return number: The random 32-bit value within the specified range. +local function random(min, max, seed) + local min = min or 0; + local max = max or 2^31-1; + if seed then + seedIsaac(seed, true); + else + seedIsaac(tostring(math.random(2^31-1)), false); + end + return (getRandom() % (max - min + 1)) + min; +end + + +--- Get a random character in printable ASCII range. +--- @return number: The random character [32, 126]. +local function getRandomChar() + return getRandom() % 95 + 32; +end + +-- Caesar-shift a character places: Generalized Vigenere +local function caesar(m, ch, shift, modulo, start) + local n + local si = 1 + if m == DECRYPT then shift = shift*-1 ; end + n = (ch - start) + shift; + if n < 0 then si,n = -1,n*-1 ; end + n = ( n % modulo ) * si; + if n < 0 then n = n + modulo ; end + return start + n; +end + +--- Encrypts a message using the ISSAC cipher algorithm. +--- @param msg string - The message to be encrypted. +--- @param key string - The key used for encryption. +--- @returns table - A table containing the encrypted message in bytes, string, and hex formats. +local function encrypt(msg, key) + seedIsaac(key, true); + local msgLength = #msg; + local destination = {}; + + for i = 1, msgLength do + destination[i] = string.char(caesar(1, msg:byte(i, i), getRandomChar(), 95, 32)); + end + + local encrypted = destination + + local public = {} + public.asBytes = function() + return encrypted + end + + public.asString = function() + return table.concat(encrypted) + end + + public.asHex = function() + return Hex.stringToHex(table.concat(encrypted)) + end + + return public +end + +--- Decrypts an encrypted message using the ISSAC cipher algorithm. +--- @param encrypted string - The encrypted message to be decrypted. +--- @param key string - The key used for encryption. +--- @returns string - The decrypted message. +local function decrypt(encrypted, key) + seedIsaac(key, true); + local msgLength = #encrypted; + local destination = {}; + + for i = 1, msgLength do + destination[i] = string.char(caesar(2, encrypted:byte(i, i), getRandomChar(), 95, 32)); + end + + return table.concat(destination); +end + +return { + seedIsaac = seedIsaac, + getRandomChar = getRandomChar, + random = random, + getRandom = getRandom, + encrypt = encrypt, + decrypt = decrypt +} \ No newline at end of file diff --git a/contract/src/crypto/cipher/mode/cbc.lua b/contract/src/crypto/cipher/mode/cbc.lua new file mode 100644 index 00000000..2879072a --- /dev/null +++ b/contract/src/crypto/cipher/mode/cbc.lua @@ -0,0 +1,171 @@ +local Array = require(".crypto.util.array"); +local Stream = require(".crypto.util.stream"); +local Queue = require(".crypto.util.queue"); + +local CBC = {}; + +CBC.Cipher = function() + + local public = {}; + + local key; + local blockCipher; + local padding; + local inputQueue; + local outputQueue; + local iv; + + public.setKey = function(keyBytes) + key = keyBytes; + return public; + end + + public.setBlockCipher = function(cipher) + blockCipher = cipher; + return public; + end + + public.setPadding = function(paddingMode) + padding = paddingMode; + return public; + end + + public.init = function() + inputQueue = Queue(); + outputQueue = Queue(); + iv = nil; + return public; + end + + public.update = function(messageStream) + local byte = messageStream(); + while (byte ~= nil) do + inputQueue.push(byte); + if(inputQueue.size() >= blockCipher.blockSize) then + local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); + + if(iv == nil) then + iv = block; + else + local out = Array.XOR(iv, block); + out = blockCipher.encrypt(key, out); + Array.writeToQueue(outputQueue, out); + iv = out; + end + end + byte = messageStream(); + end + return public; + end + + public.finish = function() + local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); + public.update(paddingStream); + + return public; + end + + public.getOutputQueue = function() + return outputQueue; + end + + public.asHex = function() + return Stream.toHex(outputQueue.pop); + end + + public.asBytes = function() + return Stream.toArray(outputQueue.pop); + end + + public.asString = function() + return Stream.toString(outputQueue.pop); + end + + return public; + +end + + +CBC.Decipher = function() + + local public = {}; + + local key; + local blockCipher; + local padding; + local inputQueue; + local outputQueue; + local iv; + + public.setKey = function(keyBytes) + key = keyBytes; + return public; + end + + public.setBlockCipher = function(cipher) + blockCipher = cipher; + return public; + end + + public.setPadding = function(paddingMode) + padding = paddingMode; + return public; + end + + public.init = function() + inputQueue = Queue(); + outputQueue = Queue(); + iv = nil; + return public; + end + + public.update = function(messageStream) + local byte = messageStream(); + while (byte ~= nil) do + inputQueue.push(byte); + if(inputQueue.size() >= blockCipher.blockSize) then + local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); + + if(iv == nil) then + iv = block; + else + local out = block; + out = blockCipher.decrypt(key, out); + out = Array.XOR(iv, out); + Array.writeToQueue(outputQueue, out); + iv = block; + end + end + byte = messageStream(); + end + return public; + end + + public.finish = function() + local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); + public.update(paddingStream); + + return public; + end + + public.getOutputQueue = function() + return outputQueue; + end + + public.asHex = function() + return Stream.toHex(outputQueue.pop); + end + + public.asBytes = function() + return Stream.toArray(outputQueue.pop); + end + + public.asString = function() + return Stream.toString(outputQueue.pop); + end + + return public; + +end + +return CBC; diff --git a/contract/src/crypto/cipher/mode/cfb.lua b/contract/src/crypto/cipher/mode/cfb.lua new file mode 100644 index 00000000..331e9152 --- /dev/null +++ b/contract/src/crypto/cipher/mode/cfb.lua @@ -0,0 +1,171 @@ +local Array = require(".crypto.util.array"); +local Stream = require(".crypto.util.stream"); +local Queue = require(".crypto.util.queue"); + +local CFB = {}; + +CFB.Cipher = function() + + local public = {}; + + local key; + local blockCipher; + local padding; + local inputQueue; + local outputQueue; + local iv; + + public.setKey = function(keyBytes) + key = keyBytes; + return public; + end + + public.setBlockCipher = function(cipher) + blockCipher = cipher; + return public; + end + + public.setPadding = function(paddingMode) + padding = paddingMode; + return public; + end + + public.init = function() + inputQueue = Queue(); + outputQueue = Queue(); + iv = nil; + return public; + end + + public.update = function(messageStream) + local byte = messageStream(); + while (byte ~= nil) do + inputQueue.push(byte); + if(inputQueue.size() >= blockCipher.blockSize) then + local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); + + if(iv == nil) then + iv = block; + else + local out = iv; + out = blockCipher.encrypt(key, out); + out = Array.XOR(out, block); + Array.writeToQueue(outputQueue, out); + iv = out; + end + end + byte = messageStream(); + end + return public; + end + + public.finish = function() + local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); + public.update(paddingStream); + + return public; + end + + public.getOutputQueue = function() + return outputQueue; + end + + public.asHex = function() + return Stream.toHex(outputQueue.pop); + end + + public.asBytes = function() + return Stream.toArray(outputQueue.pop); + end + + public.asString = function() + return Stream.toString(outputQueue.pop); + end + + return public; + +end + +CFB.Decipher = function() + + local public = {}; + + local key; + local blockCipher; + local padding; + local inputQueue; + local outputQueue; + local iv; + + public.setKey = function(keyBytes) + key = keyBytes; + return public; + end + + public.setBlockCipher = function(cipher) + blockCipher = cipher; + return public; + end + + public.setPadding = function(paddingMode) + padding = paddingMode; + return public; + end + + public.init = function() + inputQueue = Queue(); + outputQueue = Queue(); + iv = nil; + return public; + end + + public.update = function(messageStream) + local byte = messageStream(); + while (byte ~= nil) do + inputQueue.push(byte); + if(inputQueue.size() >= blockCipher.blockSize) then + local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); + + if(iv == nil) then + iv = block; + else + local out = iv; + out = blockCipher.encrypt(key, out); + out = Array.XOR(out, block); + Array.writeToQueue(outputQueue, out); + iv = block; + end + end + byte = messageStream(); + end + return public; + end + + public.finish = function() + local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); + public.update(paddingStream); + + return public; + end + + public.getOutputQueue = function() + return outputQueue; + end + + public.asHex = function() + return Stream.toHex(outputQueue.pop); + end + + public.asBytes = function() + return Stream.toArray(outputQueue.pop); + end + + public.asString = function() + return Stream.toString(outputQueue.pop); + end + + return public; + +end + +return CFB; \ No newline at end of file diff --git a/contract/src/crypto/cipher/mode/ctr.lua b/contract/src/crypto/cipher/mode/ctr.lua new file mode 100644 index 00000000..e999bce7 --- /dev/null +++ b/contract/src/crypto/cipher/mode/ctr.lua @@ -0,0 +1,252 @@ +local Array = require(".crypto.util.array"); +local Stream = require(".crypto.util.stream"); +local Queue = require(".crypto.util.queue"); + +local Bit = require(".crypto.util.bit"); + +local AND = Bit.band; + +local CTR = {}; + +CTR.Cipher = function() + + local public = {}; + + local key; + local blockCipher; + local padding; + local inputQueue; + local outputQueue; + local iv; + + public.setKey = function(keyBytes) + key = keyBytes; + return public; + end + + public.setBlockCipher = function(cipher) + blockCipher = cipher; + return public; + end + + public.setPadding = function(paddingMode) + padding = paddingMode; + return public; + end + + public.init = function() + inputQueue = Queue(); + outputQueue = Queue(); + iv = nil; + return public; + end + + local updateIV = function() + iv[16] = iv[16] + 1; + if iv[16] <= 0xFF then return; end + iv[16] = AND(iv[16], 0xFF); + + iv[15] = iv[15] + 1; + if iv[15] <= 0xFF then return; end + iv[15] = AND(iv[15], 0xFF); + + iv[14] = iv[14] + 1; + if iv[14] <= 0xFF then return; end + iv[14] = AND(iv[14], 0xFF); + + iv[13] = iv[13] + 1; + if iv[13] <= 0xFF then return; end + iv[13] = AND(iv[13], 0xFF); + + iv[12] = iv[12] + 1; + if iv[12] <= 0xFF then return; end + iv[12] = AND(iv[12], 0xFF); + + iv[11] = iv[11] + 1; + if iv[11] <= 0xFF then return; end + iv[11] = AND(iv[11], 0xFF); + + iv[10] = iv[10] + 1; + if iv[10] <= 0xFF then return; end + iv[10] = AND(iv[10], 0xFF); + + iv[9] = iv[9] + 1; + if iv[9] <= 0xFF then return; end + iv[9] = AND(iv[9], 0xFF); + + return; + end + + public.update = function(messageStream) + local byte = messageStream(); + while (byte ~= nil) do + inputQueue.push(byte); + + if(inputQueue.size() >= blockCipher.blockSize) then + local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); + + if(iv == nil) then + iv = block; + else + local out = iv; + out = blockCipher.encrypt(key, out); + + out = Array.XOR(out, block); + Array.writeToQueue(outputQueue, out); + updateIV(); + end + end + byte = messageStream(); + end + return public; + end + + public.finish = function() + local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); + public.update(paddingStream); + + return public; + end + + public.getOutputQueue = function() + return outputQueue; + end + + public.asHex = function() + return Stream.toHex(outputQueue.pop); + end + + public.asBytes = function() + return Stream.toArray(outputQueue.pop); + end + + public.asString = function() + return Stream.toString(outputQueue.pop); + end + + return public; + +end + + +CTR.Decipher = function() + + local public = {}; + + local key; + local blockCipher; + local padding; + local inputQueue; + local outputQueue; + local iv; + + public.setKey = function(keyBytes) + key = keyBytes; + return public; + end + + public.setBlockCipher = function(cipher) + blockCipher = cipher; + return public; + end + + public.setPadding = function(paddingMode) + padding = paddingMode; + return public; + end + + public.init = function() + inputQueue = Queue(); + outputQueue = Queue(); + iv = nil; + return public; + end + + local updateIV = function() + iv[16] = iv[16] + 1; + if iv[16] <= 0xFF then return; end + iv[16] = AND(iv[16], 0xFF); + + iv[15] = iv[15] + 1; + if iv[15] <= 0xFF then return; end + iv[15] = AND(iv[15], 0xFF); + + iv[14] = iv[14] + 1; + if iv[14] <= 0xFF then return; end + iv[14] = AND(iv[14], 0xFF); + + iv[13] = iv[13] + 1; + if iv[13] <= 0xFF then return; end + iv[13] = AND(iv[13], 0xFF); + + iv[12] = iv[12] + 1; + if iv[12] <= 0xFF then return; end + iv[12] = AND(iv[12], 0xFF); + + iv[11] = iv[11] + 1; + if iv[11] <= 0xFF then return; end + iv[11] = AND(iv[11], 0xFF); + + iv[10] = iv[10] + 1; + if iv[10] <= 0xFF then return; end + iv[10] = AND(iv[10], 0xFF); + + iv[9] = iv[9] + 1; + if iv[9] <= 0xFF then return; end + iv[9] = AND(iv[9], 0xFF); + + return; + end + + public.update = function(messageStream) + local byte = messageStream(); + while (byte ~= nil) do + inputQueue.push(byte); + + if(inputQueue.size() >= blockCipher.blockSize) then + local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); + + if(iv == nil) then + iv = block; + else + local out = iv; + out = blockCipher.encrypt(key, out); + + out = Array.XOR(out, block); + Array.writeToQueue(outputQueue, out); + updateIV(); + end + end + byte = messageStream(); + end + return public; + end + + public.finish = function() + local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); + public.update(paddingStream); + + return public; + end + + public.getOutputQueue = function() + return outputQueue; + end + + public.asHex = function() + return Stream.toHex(outputQueue.pop); + end + + public.asBytes = function() + return Stream.toArray(outputQueue.pop); + end + + public.asString = function() + return Stream.toString(outputQueue.pop); + end + + return public; + +end + +return CTR; diff --git a/contract/src/crypto/cipher/mode/ecb.lua b/contract/src/crypto/cipher/mode/ecb.lua new file mode 100644 index 00000000..d0f8618e --- /dev/null +++ b/contract/src/crypto/cipher/mode/ecb.lua @@ -0,0 +1,156 @@ +local Array = require(".crypto.util.array"); +local Stream = require(".crypto.util.stream"); +local Queue = require(".crypto.util.queue"); + +local ECB = {}; + +ECB.Cipher = function() + + local public = {}; + + local key; + local blockCipher; + local padding; + local inputQueue; + local outputQueue; + + public.setKey = function(keyBytes) + key = keyBytes; + return public; + end + + public.setBlockCipher = function(cipher) + blockCipher = cipher; + return public; + end + + public.setPadding = function(paddingMode) + padding = paddingMode; + return public; + end + + public.init = function() + inputQueue = Queue(); + outputQueue = Queue(); + return public; + end + + public.update = function(messageStream) + local byte = messageStream(); + while (byte ~= nil) do + inputQueue.push(byte); + if(inputQueue.size() >= blockCipher.blockSize) then + local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); + + block = blockCipher.encrypt(key, block); + + Array.writeToQueue(outputQueue, block); + end + byte = messageStream(); + end + return public; + end + + public.finish = function() + local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); + public.update(paddingStream); + + return public; + end + + public.getOutputQueue = function() + return outputQueue; + end + + public.asHex = function() + return Stream.toHex(outputQueue.pop); + end + + public.asBytes = function() + return Stream.toArray(outputQueue.pop); + end + + public.asString = function() + return Stream.toString(outputQueue.pop); + end + + return public; + +end + +ECB.Decipher = function() + + local public = {}; + + local key; + local blockCipher; + local padding; + local inputQueue; + local outputQueue; + + public.setKey = function(keyBytes) + key = keyBytes; + return public; + end + + public.setBlockCipher = function(cipher) + blockCipher = cipher; + return public; + end + + public.setPadding = function(paddingMode) + padding = paddingMode; + return public; + end + + public.init = function() + inputQueue = Queue(); + outputQueue = Queue(); + return public; + end + + public.update = function(messageStream) + local byte = messageStream(); + while (byte ~= nil) do + inputQueue.push(byte); + if(inputQueue.size() >= blockCipher.blockSize) then + local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); + + block = blockCipher.decrypt(key, block); + + Array.writeToQueue(outputQueue, block); + end + byte = messageStream(); + end + return public; + end + + public.finish = function() + local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); + public.update(paddingStream); + + return public; + end + + public.getOutputQueue = function() + return outputQueue; + end + + public.asHex = function() + return Stream.toHex(outputQueue.pop); + end + + public.asBytes = function() + return Stream.toArray(outputQueue.pop); + end + + public.asString = function() + return Stream.toString(outputQueue.pop); + end + + return public; + +end + + +return ECB; \ No newline at end of file diff --git a/contract/src/crypto/cipher/mode/ofb.lua b/contract/src/crypto/cipher/mode/ofb.lua new file mode 100644 index 00000000..adaaed16 --- /dev/null +++ b/contract/src/crypto/cipher/mode/ofb.lua @@ -0,0 +1,172 @@ +local Array = require(".crypto.util.array"); +local Stream = require(".crypto.util.stream"); +local Queue = require(".crypto.util.queue"); + +local OFB = {}; + +OFB.Cipher = function() + + local public = {}; + + local key; + local blockCipher; + local padding; + local inputQueue; + local outputQueue; + local iv; + + public.setKey = function(keyBytes) + key = keyBytes; + return public; + end + + public.setBlockCipher = function(cipher) + blockCipher = cipher; + return public; + end + + public.setPadding = function(paddingMode) + padding = paddingMode; + return public; + end + + public.init = function() + inputQueue = Queue(); + outputQueue = Queue(); + iv = nil; + return public; + end + + public.update = function(messageStream) + local byte = messageStream(); + while (byte ~= nil) do + inputQueue.push(byte); + if(inputQueue.size() >= blockCipher.blockSize) then + local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); + + if(iv == nil) then + iv = block; + else + local out = iv; + out = blockCipher.encrypt(key, out); + iv = out; + out = Array.XOR(out, block); + Array.writeToQueue(outputQueue, out); + end + end + byte = messageStream(); + end + return public; + end + + public.finish = function() + local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); + public.update(paddingStream); + + return public; + end + + public.getOutputQueue = function() + return outputQueue; + end + + public.asHex = function() + return Stream.toHex(outputQueue.pop); + end + + public.asBytes = function() + return Stream.toArray(outputQueue.pop); + end + + public.asString = function() + return Stream.toString(outputQueue.pop); + end + + return public; + +end + +OFB.Decipher = function() + + local public = {}; + + local key; + local blockCipher; + local padding; + local inputQueue; + local outputQueue; + local iv; + + public.setKey = function(keyBytes) + key = keyBytes; + return public; + end + + public.setBlockCipher = function(cipher) + blockCipher = cipher; + return public; + end + + public.setPadding = function(paddingMode) + padding = paddingMode; + return public; + end + + public.init = function() + inputQueue = Queue(); + outputQueue = Queue(); + iv = nil; + return public; + end + + public.update = function(messageStream) + local byte = messageStream(); + while (byte ~= nil) do + inputQueue.push(byte); + if(inputQueue.size() >= blockCipher.blockSize) then + local block = Array.readFromQueue(inputQueue, blockCipher.blockSize); + + if(iv == nil) then + iv = block; + else + local out = iv; + out = blockCipher.encrypt(key, out); + iv = out; + out = Array.XOR(out, block); + Array.writeToQueue(outputQueue, out); + end + end + byte = messageStream(); + end + return public; + end + + public.finish = function() + local paddingStream = padding(blockCipher.blockSize, inputQueue.getHead()); + public.update(paddingStream); + + return public; + end + + public.getOutputQueue = function() + return outputQueue; + end + + public.asHex = function() + return Stream.toHex(outputQueue.pop); + end + + public.asBytes = function() + return Stream.toArray(outputQueue.pop); + end + + public.asString = function() + return Stream.toString(outputQueue.pop); + end + + return public; + +end + + +return OFB; \ No newline at end of file diff --git a/contract/src/crypto/cipher/morus.lua b/contract/src/crypto/cipher/morus.lua new file mode 100644 index 00000000..12633378 --- /dev/null +++ b/contract/src/crypto/cipher/morus.lua @@ -0,0 +1,398 @@ +local Hex = require(".crypto.util.hex"); + +local function state_update(s, m0, m1, m2, m3) + local s00, s01, s02, s03 = s[1], s[2], s[3], s[4] + local s10, s11, s12, s13 = s[5], s[6], s[7], s[8] + local s20, s21, s22, s23 = s[9], s[10], s[11], s[12] + local s30, s31, s32, s33 = s[13], s[14], s[15], s[16] + local s40, s41, s42, s43 = s[17], s[18], s[19], s[20] + local temp + + s00 = s00 ~ s30 + s01 = s01 ~ s31 + s02 = s02 ~ s32 + s03 = s03 ~ s33 + + temp = s33 + s33 = s32 + s32 = s31 + s31 = s30 + s30 = temp + + s00 = s00 ~ s10 & s20 + s01 = s01 ~ s11 & s21 + s02 = s02 ~ s12 & s22 + s03 = s03 ~ s13 & s23 + + s00 = (s00 << 13) | (s00 >> (64-13)) --n1 + s01 = (s01 << 13) | (s01 >> (64-13)) --n1 + s02 = (s02 << 13) | (s02 >> (64-13)) --n1 + s03 = (s03 << 13) | (s03 >> (64-13)) --n1 + + + s10 = s10 ~ m0 + s11 = s11 ~ m1 + s12 = s12 ~ m2 + s13 = s13 ~ m3 + + s10 = s10 ~ s40 + s11 = s11 ~ s41 + s12 = s12 ~ s42 + s13 = s13 ~ s43 + + temp = s43 + s43 = s41 + s41 = temp + + temp = s42 + s42 = s40 + s40 = temp + + s10 = s10 ~ (s20 & s30) + s11 = s11 ~ (s21 & s31) + s12 = s12 ~ (s22 & s32) + s13 = s13 ~ (s23 & s33) + + s10 = (s10 << 46) | (s10 >> (64-46)) --n2 + s11 = (s11 << 46) | (s11 >> (64-46)) --n2 + s12 = (s12 << 46) | (s12 >> (64-46)) --n2 + s13 = (s13 << 46) | (s13 >> (64-46)) --n2 + + + s20 = s20 ~ m0 + s21 = s21 ~ m1 + s22 = s22 ~ m2 + s23 = s23 ~ m3 + + s20 = s20 ~ s00 + s21 = s21 ~ s01 + s22 = s22 ~ s02 + s23 = s23 ~ s03 + + temp = s00 + s00 = s01 + s01 = s02 + s02 = s03 + s03 = temp + + s20 = s20 ~ s30 & s40 + s21 = s21 ~ s31 & s41 + s22 = s22 ~ s32 & s42 + s23 = s23 ~ s33 & s43 + + s20 = (s20 << 38) | (s20 >> (64-38)) --n3 + s21 = (s21 << 38) | (s21 >> (64-38)) --n3 + s22 = (s22 << 38) | (s22 >> (64-38)) --n3 + s23 = (s23 << 38) | (s23 >> (64-38)) --n3 + + + s30 = s30 ~ m0 + s31 = s31 ~ m1 + s32 = s32 ~ m2 + s33 = s33 ~ m3 + + s30 = s30 ~ s10 + s31 = s31 ~ s11 + s32 = s32 ~ s12 + s33 = s33 ~ s13 + + temp = s13 + s13 = s11 + s11 = temp + + temp = s12 + s12 = s10 + s10 = temp + + s30 = s30 ~ s40 & s00 + s31 = s31 ~ s41 & s01 + s32 = s32 ~ s42 & s02 + s33 = s33 ~ s43 & s03 + + s30 = (s30 << 7) | (s30 >> (64-7)) --n4 + s31 = (s31 << 7) | (s31 >> (64-7)) --n4 + s32 = (s32 << 7) | (s32 >> (64-7)) --n4 + s33 = (s33 << 7) | (s33 >> (64-7)) --n4 + + + s40 = s40 ~ m0 + s41 = s41 ~ m1 + s42 = s42 ~ m2 + s43 = s43 ~ m3 + + s40 = s40 ~ s20 + s41 = s41 ~ s21 + s42 = s42 ~ s22 + s43 = s43 ~ s23 + + temp = s23 + s23 = s22 + s22 = s21 + s21 = s20 + s20 = temp + + s40 = s40 ~ s00 & s10 + s41 = s41 ~ s01 & s11 + s42 = s42 ~ s02 & s12 + s43 = s43 ~ s03 & s13 + + s40 = (s40 << 4) | (s40 >> (64-4)) --n5 + s41 = (s41 << 4) | (s41 >> (64-4)) --n5 + s42 = (s42 << 4) | (s42 >> (64-4)) --n5 + s43 = (s43 << 4) | (s43 >> (64-4)) --n5 + + -- update the state array + s[1], s[2], s[3], s[4] = s00, s01, s02, s03 + s[5], s[6], s[7], s[8] = s10, s11, s12, s13 + s[9], s[10], s[11], s[12] = s20, s21, s22, s23 + s[13], s[14], s[15], s[16] = s30, s31, s32, s33 + s[17], s[18], s[19], s[20] = s40, s41, s42, s43 + +end--state_update() + +local function enc_aut_step(s, m0, m1, m2, m3) + -- m0 s00 s11 s20 s30 + local c0 = m0 ~ s[1] ~ s[6] ~ (s[9] & s[13]) + -- m1 s01 s12 s21 s31 + local c1 = m1 ~ s[2] ~ s[7] ~ (s[10] & s[14]) + -- m2 s02 s13 s22 s32 + local c2 = m2 ~ s[3] ~ s[8] ~ (s[11] & s[15]) + -- m3 s03 s10 s23 s33 + local c3 = m3 ~ s[4] ~ s[5] ~ (s[12] & s[16]) + state_update(s, m0, m1, m2, m3) + return c0, c1, c2, c3 +end + +local function dec_aut_step(s, c0, c1, c2, c3, blen) + -- mlen is the length of a last partial block + -- mlen is absent/nil for full blocks + -- return the decrypted block + -- + -- m0 s00 s11 s20 s30 + local m0 = c0 ~ s[1] ~ s[6] ~ (s[9] & s[13]) + -- m1 s01 s12 s21 s31 + local m1 = c1 ~ s[2] ~ s[7] ~ (s[10] & s[14]) + -- m2 s02 s13 s22 s32 + local m2 = c2 ~ s[3] ~ s[8] ~ (s[11] & s[15]) + -- m3 s03 s10 s23 s33 + local m3 = c3 ~ s[4] ~ s[5] ~ (s[12] & s[16]) + if blen then + -- partial block => must adjust (m0, ...) before + -- updating the state + local mblk = string.pack(" 0 then ad = e:sub(1, adlen) end + local i = 1 + while i <= adlen - 31 do --process full blocks + m0, m1, m2, m3 = string.unpack("> n) | (x << (64-n)) --INLINED + -- + A = (A ~ B) ~ ((A & B) << 1) -- H(A, B); + D = D ~ A; D = (D >> 8) | (D << (56)) --ROTR64(D, 8) --R0 + C = (C ~ D) ~ ((C & D) << 1) -- H(C, D); + B = B ~ C; B = (B >> 19) | (B << (45)) --ROTR64(B, 19) --R1 + A = (A ~ B) ~ ((A & B) << 1) -- H(A, B); + D = D ~ A; D = (D >> 40) | (D << (24)) --ROTR64(D, 40) --R2 + C = (C ~ D) ~ ((C & D) << 1) -- H(C, D); + B = B ~ C; B = (B >> 63) | (B << (1)) --ROTR64(B, 63) --R3 + s[a], s[b], s[c], s[d] = A, B, C, D +end + +local function F(s) + -- The full round. s is the state: u64[16] + -- + -- beware! in Lua, arrays are 1-based indexed, not 0-indexed as in C + -- Column step + G(s, 1, 5, 9, 13); + G(s, 2, 6, 10, 14); + G(s, 3, 7, 11, 15); + G(s, 4, 8, 12, 16); + -- Diagonal step + G(s, 1, 6, 11, 16); + G(s, 2, 7, 12, 13); + G(s, 3, 8, 9, 14); + G(s, 4, 5, 10, 15); +end + +local function permute(s) + -- the core permutation (four rounds) + for _ = 1, 4 do F(s) end +end + +local function pad(ins) + -- pad string ins to length 96 ("BYTES(NORX_R)") + local out + local inslen = #ins + if inslen == 95 then return ins .. '\x81' end -- last byte is 0x01 | 0x80 + -- here inslen is < 95, so must pad with 96-(inslen+2) zeros + out = ins .. '\x01' .. string.rep('\0', 94-inslen) .. '\x80' + assert(#out == 96) + return out +end + +local function absorb_block(s, ins, ini, tag) + -- the input string is the substring of 'ins' starting at position 'ini' + -- (we cannot use a char* as in C!) + s[16] = s[16] ~ tag + permute(s) + for i = 1, 12 do + s[i] = s[i] ~ string.unpack(" 0 then + while inlen >= 96 do + absorb_block(s, ins, i, tag) + inlen = inlen - 96 + i = i + 96 + end + absorb_lastblock(s, ins:sub(i), tag) + end--if +end + +local function encrypt_data(s, out_table, ins) + local inlen = #ins + local i = 1 + if inlen > 0 then + while inlen >= 96 do + encrypt_block(s, out_table, ins, i) + inlen = inlen - 96 + i = i + 96 + end + encrypt_lastblock(s, out_table, ins:sub(i)) + end +end + +local function decrypt_data(s, out_table, ins) + local inlen = #ins + local i = 1 + if inlen > 0 then + while inlen >= 96 do + decrypt_block(s, out_table, ins, i) + inlen = inlen - 96 + i = i + 96 + end + decrypt_lastblock(s, out_table, ins:sub(i)) + end +end + +local function finalize(s, k) + -- return the authentication tag (32-byte string) + -- + s[16] = s[16] ~ FINAL_TAG + permute(s) + -- + local k1, k2, k3, k4 = string.unpack("= 32) + local out_table = {} + local state = init(key, nonce) + absorb_data(state, header, HEADER_TAG) + local ctag = crypted:sub(#crypted - 32 + 1) + local c = crypted:sub(1, #crypted - 32) + decrypt_data(state, out_table, c) + absorb_data(state, trailer, TRAILER_TAG) + local tag = finalize(state, key) + if not verify_tag(tag, ctag) then return nil, "auth failure" end + local plain = table.concat(out_table) + return plain +end + +return { + encrypt = aead_encrypt, + decrypt = aead_decrypt, + -- + key_size = 32, + nonce_size = 32, + variant = "NORX 64-4-1", +} diff --git a/contract/src/crypto/digest/blake2b.lua b/contract/src/crypto/digest/blake2b.lua new file mode 100644 index 00000000..0b68940d --- /dev/null +++ b/contract/src/crypto/digest/blake2b.lua @@ -0,0 +1,190 @@ +local Hex = require(".crypto.util.hex"); + + +local function ROTR64(x, n) + return (x >> n) | (x << (64-n)) +end + +-- G Mixing function. + +local function G(v, a, b, c, d, x, y) + v[a] = v[a] + v[b] + x + v[d] = ROTR64(v[d] ~ v[a], 32) + v[c] = v[c] + v[d] + v[b] = ROTR64(v[b] ~ v[c], 24) + v[a] = v[a] + v[b] + y + v[d] = ROTR64(v[d] ~ v[a], 16) + v[c] = v[c] + v[d] + v[b] = ROTR64(v[b] ~ v[c], 63) +end + +-- Initialization Vector. +local iv = { + 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179 +} + +local sigma = { + -- array index start at 1 in Lua, + -- => all the permutation values are incremented by one + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, + { 15, 11, 5, 9, 10, 16, 14, 7, 2, 13, 1, 3, 12, 8, 6, 4 }, + { 12, 9, 13, 1, 6, 3, 16, 14, 11, 15, 4, 7, 8, 2, 10, 5 }, + { 8, 10, 4, 2, 14, 13, 12, 15, 3, 7, 6, 11, 5, 1, 16, 9 }, + { 10, 1, 6, 8, 3, 5, 11, 16, 15, 2, 12, 13, 7, 9, 4, 14 }, + { 3, 13, 7, 11, 1, 12, 9, 4, 5, 14, 8, 6, 16, 15, 2, 10 }, + { 13, 6, 2, 16, 15, 14, 5, 11, 1, 8, 7, 4, 10, 3, 9, 12 }, + { 14, 12, 8, 15, 13, 2, 4, 10, 6, 1, 16, 5, 9, 7, 3, 11 }, + { 7, 16, 15, 10, 12, 4, 1, 9, 13, 3, 14, 8, 2, 5, 11, 6 }, + { 11, 3, 9, 5, 8, 7, 2, 6, 16, 12, 10, 15, 4, 13, 14, 1 }, + { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, + { 15, 11, 5, 9, 10, 16, 14, 7, 2, 13, 1, 3, 12, 8, 6, 4 } +} + + +local function compress(ctx, last) + -- Compression function. "last" flag indicates last block. + local v, m = {}, {} -- both v and m are u64[16] + for i = 1, 8 do + v[i] = ctx.h[i] + v[i+8] = iv[i] + end + v[13] = v[13] ~ ctx.t[1] -- low 64 bits of offset + v[14] = v[14] ~ ctx.t[2] -- high 64 bits + if last then v[15] = ~v[15] end + for i = 0, 15 do -- get little-endian words + m[i+1] = string.unpack(" 64 or (key and #key > 64) then + return nil, "illegal parameters" + end + local ctx = {h={}, t={}, c=1, outlen=outlen} -- the blake2 context + -- note: ctx.c is the index of 1st byte free in input buffer (ctx.b) + -- it is not used in this implementation + for i = 1, 8 do ctx.h[i] = iv[i] end -- state, "param block" + ctx.h[1] = ctx.h[1] ~ 0x01010000 ~ (keylen << 8) ~ outlen + ctx.t[1] = 0 --input count low word + ctx.t[2] = 0 --input count high word + -- zero input block + ctx.b = "" + if keylen > 0 then + update(ctx, key) + -- ctx.c = 128 -- pad b with zero bytes + ctx.b = ctx.b .. string.rep('\0', 128 - #ctx.b) + assert(#ctx.b == 128) + end + return ctx +end + +update = function(ctx, data) + -- buffer mgt cannot be done the C way.. + local bln, rln, iln + local i = 1 -- index of 1st byte to process in data + while true do + bln = #ctx.b -- current number of bytes in the input buffer + assert(bln <= 128) + if bln == 128 then --ctx.b is full; process it. + -- add counters + ctx.t[1] = ctx.t[1] + 128 + -- warning: this is a signed 64bit addition + -- here it is assumed that the total input is less + -- than 2^63 bytes (this should be enough for a + -- pure Lua implementation!) => ctx.t[1] overflow is ignored. + compress(ctx, false) -- false means not last + ctx.b = "" -- empty buffer + else -- ctx.b is not full; append more bytes from data + rln = 128 - bln -- remaining space (in bytes) in ctx.b + iln = #data - i + 1 -- number of bytes yet to process in data + if iln < rln then + ctx.b = ctx.b .. data:sub(i, i + iln -1) + -- here, all data bytes have been processed or put in + -- buffer and buffer is not full. we are done. + break + else + ctx.b = ctx.b .. data:sub(i, i + rln -1) + i = i + rln + end + end + end +end + +local function final(ctx) + -- finalize the hash and return the digest as a string + -- + local bln = #ctx.b + -- add number of remaining bytes in buffer (ignore carry overflow) + ctx.t[1] = ctx.t[1] + bln + -- pad the buffer with zero bytes + local rln = 128 - bln -- remaining space (in bytes) in ctx.b + ctx.b = ctx.b .. string.rep('\0', rln) + compress(ctx, true) -- true means final block + -- extract the digest (outlen bytes long) + local outtbl = {} + for i = 0, ctx.outlen - 1 do + outtbl[i+1] = string.char( + (ctx.h[(i >> 3) + 1] >> (8 * (i & 7))) & 0xff) + end + local dig = table.concat(outtbl) + return outtbl +end + +--- Calculates the BLAKE2b hash of the given data. +--- @param data string - The input data to be hashed. +--- @param outlen? number (optional) - The desired length of the hash output. Defaults to 64. +--- @param key? string (optional) - The key to be used for the hash calculation. Defaults to an empty string. +--- @returns table - A table containing the hash in bytes, string, and hex formats. +local function black2b(data, outlen, key) + local ctx, msg = init(outlen, key) + if not ctx then return ctx, msg end + update(ctx, data) + local bytes = final(ctx) + local hash = table.concat(bytes) + + local public = {} + + public.asBytes = function() + return bytes + end + + public.asString = function() + return hash + end + + public.asHex = function() + return Hex.stringToHex(hash) + end + + return public +end + +return black2b diff --git a/contract/src/crypto/digest/init.lua b/contract/src/crypto/digest/init.lua new file mode 100644 index 00000000..0646c29d --- /dev/null +++ b/contract/src/crypto/digest/init.lua @@ -0,0 +1,29 @@ +local MD2 = require(".crypto.digest.md2") +local MD4 = require(".crypto.digest.md4") +local MD5 = require(".crypto.digest.md5") +local SHA1 = require(".crypto.digest.sha1") +local SHA2_256 = require(".crypto.digest.sha2_256") +local SHA2_512 = require(".crypto.digest.sha2_512") +local SHA3 = require(".crypto.digest.sha3") +local Blake2b = require(".crypto.digest.blake2b") + + +local digest = { + _version = "0.0.1", + md2 = MD2, + md4 = MD4, + md5 = MD5, + sha1 = SHA1.sha1, + sha2_256 = SHA2_256.sha2_256, + sha2_512 = SHA2_512, + sha3_256 = SHA3.sha3_256, + sha3_512 = SHA3.sha3_512, + keccak256 = SHA3.keccak256, + keccak512 = SHA3.keccak512, + blake2b = Blake2b +} + + + + +return digest diff --git a/contract/src/crypto/digest/md2.lua b/contract/src/crypto/digest/md2.lua new file mode 100644 index 00000000..87fede00 --- /dev/null +++ b/contract/src/crypto/digest/md2.lua @@ -0,0 +1,112 @@ +local Bit = require(".crypto.util.bit"); +local Queue = require(".crypto.util.queue"); + +local SUBST = { + 0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01, 0x3D, 0x36, 0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13, + 0x62, 0xA7, 0x05, 0xF3, 0xC0, 0xC7, 0x73, 0x8C, 0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C, 0x82, 0xCA, + 0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16, 0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12, + 0xBE, 0x4E, 0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49, 0xA0, 0xFB, 0xF5, 0x8E, 0xBB, 0x2F, 0xEE, 0x7A, + 0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2, 0x07, 0x3F, 0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21, + 0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27, 0x35, 0x3E, 0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03, + 0xFF, 0x19, 0x30, 0xB3, 0x48, 0xA5, 0xB5, 0xD1, 0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56, 0xAA, 0xC6, + 0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6, 0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1, + 0x45, 0x9D, 0x70, 0x59, 0x64, 0x71, 0x87, 0x20, 0x86, 0x5B, 0xCF, 0x65, 0xE6, 0x2D, 0xA8, 0x02, + 0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0, 0xB9, 0xF6, 0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F, + 0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A, 0xC3, 0x5C, 0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26, + 0x2C, 0x53, 0x0D, 0x6E, 0x85, 0x28, 0x84, 0x09, 0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81, 0x4D, 0x52, + 0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA, 0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A, + 0x78, 0x88, 0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D, 0xE9, 0xCB, 0xD5, 0xFE, 0x3B, 0x00, 0x1D, 0x39, + 0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58, 0xD0, 0xE4, 0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A, + 0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A, 0xDB, 0x99, 0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14 }; + +local XOR = Bit.bxor; + +local MD2 = function(stream) + local queue = Queue(); + local public = {} + + local X = {}; + for i = 0, 47 do + X[i] = 0x00; + end + + local C = {}; + for i = 0, 15 do + C[i] = 0x00; + end + + local processBlock = function() + local block = {}; + + for i = 0, 15 do + block[i] = queue.pop(); + end + + for i = 0, 15 do + X[i + 16] = block[i]; + X[i + 32] = XOR(X[i], block[i]); --mix + end + + local t; + + --update block + t = 0; + for i = 0, 17 do + for j = 0, 47 do + X[j] = XOR(X[j], SUBST[t + 1]); + t = X[j]; + end + t = (t + i) % 256; + end + + --update checksum + t = C[15]; + for i = 0, 15 do + C[i] = XOR(C[i], SUBST[XOR(block[i], t) + 1]); + t = C[i]; + end + + end + + for b in stream do + queue.push(b); + if(queue.size() >= 16) then processBlock(); end + end + + local i = 16 - queue.size(); + + while queue.size() < 16 do + queue.push(i); + end + + processBlock(); + + queue.push(C[ 0]); queue.push(C[ 1]); queue.push(C[ 2]); queue.push(C[ 3]); + queue.push(C[ 4]); queue.push(C[ 5]); queue.push(C[ 6]); queue.push(C[ 7]); + queue.push(C[ 8]); queue.push(C[ 9]); queue.push(C[10]); queue.push(C[11]); + queue.push(C[12]); queue.push(C[13]); queue.push(C[14]); queue.push(C[15]); + + processBlock(); + + public.asBytes = function() + return {X[ 0], X[ 1], X[ 2], X[ 3], X[ 4], X[ 5], X[ 6], X[ 7], + X[ 8], X[ 9], X[10], X[11], X[12], X[13], X[14], X[15]}; + end + + public.asHex = function() + return string.format("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + X[ 0], X[ 1], X[ 2], X[ 3], X[ 4], X[ 5], X[ 6], X[ 7], + X[ 8], X[ 9], X[10], X[11], X[12], X[13], X[14], X[15]); + end + + public.asString = function() + return string.pack(string.rep('B', 16), + X[ 0], X[ 1], X[ 2], X[ 3], X[ 4], X[ 5], X[ 6], X[ 7], + X[ 8], X[ 9], X[10], X[11], X[12], X[13], X[14], X[15]); + end + + return public; + +end + +return MD2; \ No newline at end of file diff --git a/contract/src/crypto/digest/md4.lua b/contract/src/crypto/digest/md4.lua new file mode 100644 index 00000000..eee89a72 --- /dev/null +++ b/contract/src/crypto/digest/md4.lua @@ -0,0 +1,193 @@ +local Bit = require(".crypto.util.bit"); +local Queue = require(".crypto.util.queue"); + +local AND = Bit.band; +local OR = Bit.bor; +local NOT = Bit.bnot; +local XOR = Bit.bxor; +local LROT = Bit.lrotate; +local LSHIFT = Bit.lshift; +local RSHIFT = Bit.rshift; + +--MD4 is little-endian +local bytes2word = function(b0, b1, b2, b3) + local i = b3; i = LSHIFT(i, 8); + i = OR(i, b2); i = LSHIFT(i, 8); + i = OR(i, b1); i = LSHIFT(i, 8); + i = OR(i, b0); + return i; +end + +local word2bytes = function(word) + local b0, b1, b2, b3; + b0 = AND(word, 0xFF); word = RSHIFT(word, 8); + b1 = AND(word, 0xFF); word = RSHIFT(word, 8); + b2 = AND(word, 0xFF); word = RSHIFT(word, 8); + b3 = AND(word, 0xFF); + return b0, b1, b2, b3; +end + +local dword2bytes = function(i) + local b4, b5, b6, b7 = word2bytes(math.floor(i / 0x100000000)); + local b0, b1, b2, b3 = word2bytes(i); + return b0, b1, b2, b3, b4, b5, b6, b7; +end + +local F = function(x, y, z) return OR(AND(x, y), AND(NOT(x), z)); end +local G = function(x, y, z) return OR(AND(x, y), OR(AND(x, z), AND(y, z))); end +local H = function(x, y, z) return XOR(x, XOR(y, z)); end + + +local MD4 = function(stream) + + local queue = Queue(); + + local A = 0x67452301; + local B = 0xefcdab89; + local C = 0x98badcfe; + local D = 0x10325476; + local public = {}; + + local processBlock = function() + local a = A; + local b = B; + local c = C; + local d = D; + + local X = {}; + + for i = 0, 15 do + X[i] = bytes2word(queue.pop(), queue.pop(), queue.pop(), queue.pop()); + end + + a = LROT(a + F(b, c, d) + X[0], 3); + d = LROT(d + F(a, b, c) + X[1], 7); + c = LROT(c + F(d, a, b) + X[2], 11); + b = LROT(b + F(c, d, a) + X[3], 19); + + a = LROT(a + F(b, c, d) + X[4], 3); + d = LROT(d + F(a, b, c) + X[5], 7); + c = LROT(c + F(d, a, b) + X[6], 11); + b = LROT(b + F(c, d, a) + X[7], 19); + + a = LROT(a + F(b, c, d) + X[8], 3); + d = LROT(d + F(a, b, c) + X[9], 7); + c = LROT(c + F(d, a, b) + X[10], 11); + b = LROT(b + F(c, d, a) + X[11], 19); + + a = LROT(a + F(b, c, d) + X[12], 3); + d = LROT(d + F(a, b, c) + X[13], 7); + c = LROT(c + F(d, a, b) + X[14], 11); + b = LROT(b + F(c, d, a) + X[15], 19); + + + a = LROT(a + G(b, c, d) + X[0] + 0x5A827999, 3); + d = LROT(d + G(a, b, c) + X[4] + 0x5A827999, 5); + c = LROT(c + G(d, a, b) + X[8] + 0x5A827999, 9); + b = LROT(b + G(c, d, a) + X[12] + 0x5A827999, 13); + + a = LROT(a + G(b, c, d) + X[1] + 0x5A827999, 3); + d = LROT(d + G(a, b, c) + X[5] + 0x5A827999, 5); + c = LROT(c + G(d, a, b) + X[9] + 0x5A827999, 9); + b = LROT(b + G(c, d, a) + X[13] + 0x5A827999, 13); + + a = LROT(a + G(b, c, d) + X[2] + 0x5A827999, 3); + d = LROT(d + G(a, b, c) + X[6] + 0x5A827999, 5); + c = LROT(c + G(d, a, b) + X[10] + 0x5A827999, 9); + b = LROT(b + G(c, d, a) + X[14] + 0x5A827999, 13); + + a = LROT(a + G(b, c, d) + X[3] + 0x5A827999, 3); + d = LROT(d + G(a, b, c) + X[7] + 0x5A827999, 5); + c = LROT(c + G(d, a, b) + X[11] + 0x5A827999, 9); + b = LROT(b + G(c, d, a) + X[15] + 0x5A827999, 13); + + + a = LROT(a + H(b, c, d) + X[0] + 0x6ED9EBA1, 3); + d = LROT(d + H(a, b, c) + X[8] + 0x6ED9EBA1, 9); + c = LROT(c + H(d, a, b) + X[4] + 0x6ED9EBA1, 11); + b = LROT(b + H(c, d, a) + X[12] + 0x6ED9EBA1, 15); + + a = LROT(a + H(b, c, d) + X[2] + 0x6ED9EBA1, 3); + d = LROT(d + H(a, b, c) + X[10] + 0x6ED9EBA1, 9); + c = LROT(c + H(d, a, b) + X[6] + 0x6ED9EBA1, 11); + b = LROT(b + H(c, d, a) + X[14] + 0x6ED9EBA1, 15); + + a = LROT(a + H(b, c, d) + X[1] + 0x6ED9EBA1, 3); + d = LROT(d + H(a, b, c) + X[9] + 0x6ED9EBA1, 9); + c = LROT(c + H(d, a, b) + X[5] + 0x6ED9EBA1, 11); + b = LROT(b + H(c, d, a) + X[13] + 0x6ED9EBA1, 15); + + a = LROT(a + H(b, c, d) + X[3] + 0x6ED9EBA1, 3); + d = LROT(d + H(a, b, c) + X[11] + 0x6ED9EBA1, 9); + c = LROT(c + H(d, a, b) + X[7] + 0x6ED9EBA1, 11); + b = LROT(b + H(c, d, a) + X[15] + 0x6ED9EBA1, 15); + + + A = AND(A + a, 0xFFFFFFFF); + B = AND(B + b, 0xFFFFFFFF); + C = AND(C + c, 0xFFFFFFFF); + D = AND(D + d, 0xFFFFFFFF); + end + + + for s in stream do + queue.push(s); + if (queue.size() >= 64) then processBlock(); end + end + + local bits = queue.getHead() * 8; + + queue.push(0x80); + while ((queue.size() + 7) % 64) < 63 do + queue.push(0x00); + end + + local b0, b1, b2, b3, b4, b5, b6, b7 = dword2bytes(bits); + + queue.push(b0); + queue.push(b1); + queue.push(b2); + queue.push(b3); + queue.push(b4); + queue.push(b5); + queue.push(b6); + queue.push(b7); + + while queue.size() > 0 do + processBlock(); + end + + public.asBytes = function() + local b0, b1, b2, b3 = word2bytes(A); + local b4, b5, b6, b7 = word2bytes(B); + local b8, b9, b10, b11 = word2bytes(C); + local b12, b13, b14, b15 = word2bytes(D); + + return {b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15}; + end + + public.asHex = function() + local b0, b1, b2, b3 = word2bytes(A); + local b4, b5, b6, b7 = word2bytes(B); + local b8, b9, b10, b11 = word2bytes(C); + local b12, b13, b14, b15 = word2bytes(D); + + return string.format("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15); + end + + public.asString = function() + local b0, b1, b2, b3 = word2bytes(A); + local b4, b5, b6, b7 = word2bytes(B); + local b8, b9, b10, b11 = word2bytes(C); + local b12, b13, b14, b15 = word2bytes(D); + + return string.pack(string.rep('B', 16), + b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15); + end + + return public; + +end + +return MD4; \ No newline at end of file diff --git a/contract/src/crypto/digest/md5.lua b/contract/src/crypto/digest/md5.lua new file mode 100644 index 00000000..be40518a --- /dev/null +++ b/contract/src/crypto/digest/md5.lua @@ -0,0 +1,178 @@ +local Bit = require(".crypto.util.bit"); +local Queue = require(".crypto.util.queue"); + +local SHIFT = { + 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, + 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, + 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, + 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; + +local CONSTANTS = { + 0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, + 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, + 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, + 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, + 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, + 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, + 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, + 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, + 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, + 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, + 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, + 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, + 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, + 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, + 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, + 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}; + +local AND = Bit.band; +local OR = Bit.bor; +local NOT = Bit.bnot; +local XOR = Bit.bxor; +local LROT = Bit.lrotate; +local LSHIFT = Bit.lshift; +local RSHIFT = Bit.rshift; + +--MD5 is little-endian +local bytes2word = function(b0, b1, b2, b3) + local i = b3; i = LSHIFT(i, 8); + i = OR(i, b2); i = LSHIFT(i, 8); + i = OR(i, b1); i = LSHIFT(i, 8); + i = OR(i, b0); + return i; +end + +local word2bytes = function(word) + local b0, b1, b2, b3; + b0 = AND(word, 0xFF); word = RSHIFT(word, 8); + b1 = AND(word, 0xFF); word = RSHIFT(word, 8); + b2 = AND(word, 0xFF); word = RSHIFT(word, 8); + b3 = AND(word, 0xFF); + return b0, b1, b2, b3; +end + +local dword2bytes = function(i) + local b4, b5, b6, b7 = word2bytes(math.floor(i / 0x100000000)); + local b0, b1, b2, b3 = word2bytes(i); + return b0, b1, b2, b3, b4, b5, b6, b7; +end + +local F = function(x, y, z) return OR(AND(x, y), AND(NOT(x), z)); end +local G = function(x, y, z) return OR(AND(x, z), AND(y, NOT(z))); end +local H = function(x, y, z) return XOR(x, XOR(y, z)); end +local I = function(x, y, z) return XOR(y, OR(x, NOT(z))); end + +local MD5 = function(stream) + + local queue = Queue(); + + local A = 0x67452301; + local B = 0xefcdab89; + local C = 0x98badcfe; + local D = 0x10325476; + local public = {}; + + local processBlock = function() + local a = A; + local b = B; + local c = C; + local d = D; + + local X = {}; + + for i = 1, 16 do + X[i] = bytes2word(queue.pop(), queue.pop(), queue.pop(), queue.pop()); + end + + for i = 0, 63 do + local f, g, temp; + + if (0 <= i) and (i <= 15) then + f = F(b, c, d); + g = i; + elseif (16 <= i) and (i <= 31) then + f = G(b, c, d); + g = (5 * i + 1) % 16; + elseif (32 <= i) and (i <= 47) then + f = H(b, c, d); + g = (3 * i + 5) % 16; + elseif (48 <= i) and (i <= 63) then + f = I(b, c, d); + g = (7 * i) % 16; + end + temp = d; + d = c; + c = b; + b = b + LROT((a + f + CONSTANTS[i + 1] + X[g + 1]), SHIFT[i + 1]); + a = temp; + end + + A = AND(A + a, 0xFFFFFFFF); + B = AND(B + b, 0xFFFFFFFF); + C = AND(C + c, 0xFFFFFFFF); + D = AND(D + d, 0xFFFFFFFF); + end + + for s in stream do + queue.push(s); + if (queue.size() >= 64) then processBlock(); end + end + + local bits = queue.getHead() * 8; + + queue.push(0x80); + while ((queue.size() + 7) % 64) < 63 do + queue.push(0x00); + end + + local b0, b1, b2, b3, b4, b5, b6, b7 = dword2bytes(bits); + + queue.push(b0); + queue.push(b1); + queue.push(b2); + queue.push(b3); + queue.push(b4); + queue.push(b5); + queue.push(b6); + queue.push(b7); + + while queue.size() > 0 do + processBlock(); + end + + public.asBytes = function() + local b0, b1, b2, b3 = word2bytes(A); + local b4, b5, b6, b7 = word2bytes(B); + local b8, b9, b10, b11 = word2bytes(C); + local b12, b13, b14, b15 = word2bytes(D); + + return {b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15}; + end + + public.asHex = function() + local b0, b1, b2, b3 = word2bytes(A); + local b4, b5, b6, b7 = word2bytes(B); + local b8, b9, b10, b11 = word2bytes(C); + local b12, b13, b14, b15 = word2bytes(D); + + return string.format("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15); + end + + public.asString = function() + local b0, b1, b2, b3 = word2bytes(A); + local b4, b5, b6, b7 = word2bytes(B); + local b8, b9, b10, b11 = word2bytes(C); + local b12, b13, b14, b15 = word2bytes(D); + + return string.pack(string.rep('B', 16), + b0, b1, b2, b3, b4, b5, b6, b7, b8, + b9, b10, b11, b12, b13, b14, b15 + ) + end + + return public; + +end + +return MD5; \ No newline at end of file diff --git a/contract/src/crypto/digest/sha1.lua b/contract/src/crypto/digest/sha1.lua new file mode 100644 index 00000000..589acf8f --- /dev/null +++ b/contract/src/crypto/digest/sha1.lua @@ -0,0 +1,191 @@ +local Bit = require(".crypto.util.bit"); +local Queue = require(".crypto.util.queue"); + +local AND = Bit.band; +local OR = Bit.bor; +local XOR = Bit.bxor; +local LROT = Bit.lrotate; +local LSHIFT = Bit.lshift; +local RSHIFT = Bit.rshift; + +--SHA1 is big-endian +local bytes2word = function(b0, b1, b2, b3) + local i = b0; i = LSHIFT(i, 8); + i = OR(i, b1); i = LSHIFT(i, 8); + i = OR(i, b2); i = LSHIFT(i, 8); + i = OR(i, b3); + return i; +end + +local word2bytes = function(word) + local b0, b1, b2, b3; + b3 = AND(word, 0xFF); word = RSHIFT(word, 8); + b2 = AND(word, 0xFF); word = RSHIFT(word, 8); + b1 = AND(word, 0xFF); word = RSHIFT(word, 8); + b0 = AND(word, 0xFF); + return b0, b1, b2, b3; +end + +local dword2bytes = function(i) + local b4, b5, b6, b7 = word2bytes(i); + local b0, b1, b2, b3 = word2bytes(math.floor(i / 0x100000000)); + return b0, b1, b2, b3, b4, b5, b6, b7; +end + +local F = function(x, y, z) return XOR(z, AND(x, XOR(y, z))); end +local G = function(x, y, z) return XOR(x, XOR(y, z)); end +local H = function(x, y, z) return OR(AND(x, OR(y, z)), AND(y, z)); end +local I = function(x, y, z) return XOR(x, XOR(y, z)); end + +local SHA1 = function() + + local queue = Queue(); + + local h0 = 0x67452301; + local h1 = 0xEFCDAB89; + local h2 = 0x98BADCFE; + local h3 = 0x10325476; + local h4 = 0xC3D2E1F0; + + local public = {}; + + local processBlock = function() + local a = h0; + local b = h1; + local c = h2; + local d = h3; + local e = h4; + local temp; + local k; + + local w = {}; + for i = 0, 15 do + w[i] = bytes2word(queue.pop(), queue.pop(), queue.pop(), queue.pop()); + end + + for i = 16, 79 do + w[i] = LROT((XOR(XOR(w[i - 3], w[i - 8]), XOR(w[i - 14], w[i - 16]))), 1); + end + + for i = 0, 79 do + if (i <= 19) then + temp = F(b, c, d); + k = 0x5A827999; + elseif (i <= 39) then + temp = G(b, c, d); + k = 0x6ED9EBA1; + elseif (i <= 59) then + temp = H(b, c, d); + k = 0x8F1BBCDC; + else + temp = I(b, c, d); + k = 0xCA62C1D6; + end + temp = LROT(a, 5) + temp + e + k + w[i]; + e = d; + d = c; + c = LROT(b, 30); + b = a; + a = temp; + end + + h0 = AND(h0 + a, 0xFFFFFFFF); + h1 = AND(h1 + b, 0xFFFFFFFF); + h2 = AND(h2 + c, 0xFFFFFFFF); + h3 = AND(h3 + d, 0xFFFFFFFF); + h4 = AND(h4 + e, 0xFFFFFFFF); + end + + public.init = function() + queue.reset(); + h0 = 0x67452301; + h1 = 0xEFCDAB89; + h2 = 0x98BADCFE; + h3 = 0x10325476; + h4 = 0xC3D2E1F0; + return public; + end + + + public.update = function(bytes) + for b in bytes do + queue.push(b); + if queue.size() >= 64 then processBlock(); end + end + + return public; + end + + public.finish = function() + local bits = queue.getHead() * 8; + + queue.push(0x80); + while ((queue.size() + 7) % 64) < 63 do + queue.push(0x00); + end + + local b0, b1, b2, b3, b4, b5, b6, b7 = dword2bytes(bits); + + queue.push(b0); + queue.push(b1); + queue.push(b2); + queue.push(b3); + queue.push(b4); + queue.push(b5); + queue.push(b6); + queue.push(b7); + + while queue.size() > 0 do + processBlock(); + end + + return public; + end + + public.asBytes = function() + local b0, b1, b2, b3 = word2bytes(h0); + local b4, b5, b6, b7 = word2bytes(h1); + local b8, b9, b10, b11 = word2bytes(h2); + local b12, b13, b14, b15 = word2bytes(h3); + local b16, b17, b18, b19 = word2bytes(h4); + + return {b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19}; + end + + public.asHex = function() + local b0, b1, b2, b3 = word2bytes(h0); + local b4, b5, b6, b7 = word2bytes(h1); + local b8, b9, b10, b11 = word2bytes(h2); + local b12, b13, b14, b15 = word2bytes(h3); + local b16, b17, b18, b19 = word2bytes(h4); + + return string.format("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19); + end + + public.asString = function() + local b0, b1, b2, b3 = word2bytes(h0); + local b4, b5, b6, b7 = word2bytes(h1); + local b8, b9, b10, b11 = word2bytes(h2); + local b12, b13, b14, b15 = word2bytes(h3); + local b16, b17, b18, b19 = word2bytes(h4); + + return string.pack(string.rep('B', 20), + b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16, b17, b18, b19); + end + + return public; +end + + +local sha1 = function(stream) + local result = SHA1() + .update(stream) + .finish() + return result +end + +return { + sha1 = sha1, + SHA1 = SHA1 +} \ No newline at end of file diff --git a/contract/src/crypto/digest/sha2_256.lua b/contract/src/crypto/digest/sha2_256.lua new file mode 100644 index 00000000..64be951d --- /dev/null +++ b/contract/src/crypto/digest/sha2_256.lua @@ -0,0 +1,230 @@ +local Bit = require(".crypto.util.bit"); +local Queue = require(".crypto.util.queue"); +local Stream = require(".crypto.util.stream"); + +local CONSTANTS = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; + +local fmt = "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" .. + "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" + +local AND = Bit.band; +local OR = Bit.bor; +local NOT = Bit.bnot; +local XOR = Bit.bxor; +local RROT = Bit.rrotate; +local LSHIFT = Bit.lshift; +local RSHIFT = Bit.rshift; + +--SHA2 is big-endian +local bytes2word = function(b0, b1, b2, b3) + local i = b0; i = LSHIFT(i, 8); + i = OR(i, b1); i = LSHIFT(i, 8); + i = OR(i, b2); i = LSHIFT(i, 8); + i = OR(i, b3); + return i; +end + +local word2bytes = function(word) + local b0, b1, b2, b3; + b3 = AND(word, 0xFF); word = RSHIFT(word, 8); + b2 = AND(word, 0xFF); word = RSHIFT(word, 8); + b1 = AND(word, 0xFF); word = RSHIFT(word, 8); + b0 = AND(word, 0xFF); + return b0, b1, b2, b3; +end + +local dword2bytes = function(i) + local b4, b5, b6, b7 = word2bytes(i); + local b0, b1, b2, b3 = word2bytes(math.floor(i / 0x100000000)); + return b0, b1, b2, b3, b4, b5, b6, b7; +end + + +local SHA2_256 = function() + + local queue = Queue(); + + local h0 = 0x6a09e667; + local h1 = 0xbb67ae85; + local h2 = 0x3c6ef372; + local h3 = 0xa54ff53a; + local h4 = 0x510e527f; + local h5 = 0x9b05688c; + local h6 = 0x1f83d9ab; + local h7 = 0x5be0cd19; + + local public = {}; + + local processBlock = function() + local a = h0; + local b = h1; + local c = h2; + local d = h3; + local e = h4; + local f = h5; + local g = h6; + local h = h7; + + local w = {}; + + for i = 0, 15 do + w[i] = bytes2word(queue.pop(), queue.pop(), queue.pop(), queue.pop()); + end + + for i = 16, 63 do + local s0 = XOR(RROT(w[i - 15], 7), XOR(RROT(w[i - 15], 18), RSHIFT(w[i - 15], 3))); + local s1 = XOR(RROT(w[i - 2], 17), XOR(RROT(w[i - 2], 19), RSHIFT(w[i - 2], 10))); + w[i] = AND(w[i - 16] + s0 + w[i - 7] + s1, 0xFFFFFFFF); + end + + for i = 0, 63 do + local s1 = XOR(RROT(e, 6), XOR(RROT(e, 11), RROT(e, 25))); + local ch = XOR(AND(e, f), AND(NOT(e), g)); + local temp1 = h + s1 + ch + CONSTANTS[i + 1] + w[i]; + local s0 = XOR(RROT(a, 2), XOR(RROT(a, 13), RROT(a, 22))); + local maj = XOR(AND(a, b), XOR(AND(a, c), AND(b, c))); + local temp2 = s0 + maj; + + h = g; + g = f; + f = e; + e = d + temp1; + d = c; + c = b; + b = a; + a = temp1 + temp2; + end + + h0 = AND(h0 + a, 0xFFFFFFFF); + h1 = AND(h1 + b, 0xFFFFFFFF); + h2 = AND(h2 + c, 0xFFFFFFFF); + h3 = AND(h3 + d, 0xFFFFFFFF); + h4 = AND(h4 + e, 0xFFFFFFFF); + h5 = AND(h5 + f, 0xFFFFFFFF); + h6 = AND(h6 + g, 0xFFFFFFFF); + h7 = AND(h7 + h, 0xFFFFFFFF); + end + + public.init = function() + queue.reset(); + + h0 = 0x6a09e667; + h1 = 0xbb67ae85; + h2 = 0x3c6ef372; + h3 = 0xa54ff53a; + h4 = 0x510e527f; + h5 = 0x9b05688c; + h6 = 0x1f83d9ab; + h7 = 0x5be0cd19; + + return public; + end + + public.update = function(bytes) + for b in bytes do + queue.push(b); + if queue.size() >= 64 then processBlock(); end + end + + return public; + end + + public.finish = function() + local bits = queue.getHead() * 8; + + queue.push(0x80); + while ((queue.size() + 7) % 64) < 63 do + queue.push(0x00); + end + + local b0, b1, b2, b3, b4, b5, b6, b7 = dword2bytes(bits); + + queue.push(b0); + queue.push(b1); + queue.push(b2); + queue.push(b3); + queue.push(b4); + queue.push(b5); + queue.push(b6); + queue.push(b7); + + while queue.size() > 0 do + processBlock(); + end + + return public; + end + + public.asBytes = function() + local b0, b1, b2, b3 = word2bytes(h0); + local b4, b5, b6, b7 = word2bytes(h1); + local b8, b9, b10, b11 = word2bytes(h2); + local b12, b13, b14, b15 = word2bytes(h3); + local b16, b17, b18, b19 = word2bytes(h4); + local b20, b21, b22, b23 = word2bytes(h5); + local b24, b25, b26, b27 = word2bytes(h6); + local b28, b29, b30, b31 = word2bytes(h7); + + + return { b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 + , b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31}; + end + + public.asHex = function() + local b0, b1, b2, b3 = word2bytes(h0); + local b4, b5, b6, b7 = word2bytes(h1); + local b8, b9, b10, b11 = word2bytes(h2); + local b12, b13, b14, b15 = word2bytes(h3); + local b16, b17, b18, b19 = word2bytes(h4); + local b20, b21, b22, b23 = word2bytes(h5); + local b24, b25, b26, b27 = word2bytes(h6); + local b28, b29, b30, b31 = word2bytes(h7); + + return string.format(fmt, b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15 + , b16, b17, b18, b19, b20, b21, b22, b23, b24, b25, b26, b27, b28, b29, b30, b31); + end + + public.asString = function() + local b0, b1, b2, b3 = word2bytes(h0); + local b4, b5, b6, b7 = word2bytes(h1); + local b8, b9, b10, b11 = word2bytes(h2); + local b12, b13, b14, b15 = word2bytes(h3); + local b16, b17, b18, b19 = word2bytes(h4); + local b20, b21, b22, b23 = word2bytes(h5); + local b24, b25, b26, b27 = word2bytes(h6); + local b28, b29, b30, b31 = word2bytes(h7); + + return string.pack(string.rep('B', 32), + b0, b1, b2, b3, b4, b5, b6, b7, b8, + b9, b10, b11, b12, b13, b14, b15, + b16, b17, b18, b19, b20, b21, b22, b23, b24, + b25, b26, b27, b28, b29, b30, b31); + end + + return public; + +end + +--- @class Stream : table + +--- @param stream (Stream) - A function that returns the next byte of the stream, or nil if the stream has ended. +--- @returns table - A table containing the hash in bytes, string, and hex formats. +local sha2_256 = function(stream) + local result = SHA2_256() + .update(stream) + .finish() + return result +end + +return { + sha2_256 = sha2_256, + SHA2_256 = SHA2_256 +}; diff --git a/contract/src/crypto/digest/sha2_512.lua b/contract/src/crypto/digest/sha2_512.lua new file mode 100644 index 00000000..56298c70 --- /dev/null +++ b/contract/src/crypto/digest/sha2_512.lua @@ -0,0 +1,107 @@ +local Hex = require(".crypto.util.hex") + +local k512 = { +0x428a2f98d728ae22,0x7137449123ef65cd,0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc, +0x3956c25bf348b538,0x59f111f1b605d019,0x923f82a4af194f9b,0xab1c5ed5da6d8118, +0xd807aa98a3030242,0x12835b0145706fbe,0x243185be4ee4b28c,0x550c7dc3d5ffb4e2, +0x72be5d74f27b896f,0x80deb1fe3b1696b1,0x9bdc06a725c71235,0xc19bf174cf692694, +0xe49b69c19ef14ad2,0xefbe4786384f25e3,0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65, +0x2de92c6f592b0275,0x4a7484aa6ea6e483,0x5cb0a9dcbd41fbd4,0x76f988da831153b5, +0x983e5152ee66dfab,0xa831c66d2db43210,0xb00327c898fb213f,0xbf597fc7beef0ee4, +0xc6e00bf33da88fc2,0xd5a79147930aa725,0x06ca6351e003826f,0x142929670a0e6e70, +0x27b70a8546d22ffc,0x2e1b21385c26c926,0x4d2c6dfc5ac42aed,0x53380d139d95b3df, +0x650a73548baf63de,0x766a0abb3c77b2a8,0x81c2c92e47edaee6,0x92722c851482353b, +0xa2bfe8a14cf10364,0xa81a664bbc423001,0xc24b8b70d0f89791,0xc76c51a30654be30, +0xd192e819d6ef5218,0xd69906245565a910,0xf40e35855771202a,0x106aa07032bbd1b8, +0x19a4c116b8d2d0c8,0x1e376c085141ab53,0x2748774cdf8eeb99,0x34b0bcb5e19b48a8, +0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb,0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3, +0x748f82ee5defb2fc,0x78a5636f43172f60,0x84c87814a1f0ab72,0x8cc702081a6439ec, +0x90befffa23631e28,0xa4506cebde82bde9,0xbef9a3f7b2c67915,0xc67178f2e372532b, +0xca273eceea26619c,0xd186b8c721c0c207,0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178, +0x06f067aa72176fba,0x0a637dc5a2c898a6,0x113f9804bef90dae,0x1b710b35131c471b, +0x28db77f523047d84,0x32caab7b40c72493,0x3c9ebe0a15c9bebc,0x431d67c49c100d4c, +0x4cc5d4becb3e42b6,0x597f299cfc657e2a,0x5fcb6fab3ad6faec,0x6c44198c4a475817 +} + +local function pad128(msg, len) + local extra = 128 - ((len + 1 + 8) % 128) + len = string.pack(">I8", len * 8) + msg = msg .. "\128" .. string.rep("\0", extra) .. len + assert(#msg % 128 == 0) + return msg +end + +local ww512 = {} + +--- SHA-512 hash function. +--- @param msg string - The message to hash. +--- @returns table - A table containing the hash in bytes, string, and hex formats. +local function sha512 (msg) + msg = pad128(msg, #msg) + local h1, h2, h3, h4, h5, h6, h7, h8 = + 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, + 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, + 0x510e527fade682d1, 0x9b05688c2b3e6c1f, + 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179 + local k = k512 + local w = ww512 + local mlen = #msg + + for i = 1, mlen, 128 do + w[1], w[2], w[3], w[4], w[5], w[6], w[7], w[8], + w[9], w[10], w[11], w[12], w[13], w[14], w[15], w[16] + = string.unpack(">i8i8i8i8i8i8i8i8i8i8i8i8i8i8i8i8", msg, i) + -- mix msg block in state + + for j = 17, 80 do + local a = w[j-15] + local b = w[j-2] + w[j] = (a >> 1 ~ a >> 7 ~ a >> 8 ~ a << 56 ~ a << 63) + + (b >> 6 ~ b >> 19 ~ b >> 61 ~ b << 3 ~ b << 45) + + w[j-7] + w[j-16] + end + local a, b, c, d, e, f, g, h = h1, h2, h3, h4, h5, h6, h7, h8 + -- main state permutation + for j = 1, 80 do + local z = (e >> 14 ~ e >> 18 ~ e >> 41 ~ e << 23 + ~ e << 46 ~ e << 50) + + (g ~ e & (f ~ g)) + h + k[j] + w[j] + h = g + g = f + f = e + e = z + d + d = c + c = b + b = a + a = z + ((a ~ c) & d ~ a & c) + + (a >> 28 ~ a >> 34 ~ a >> 39 ~ a << 25 + ~ a << 30 ~ a << 36) + end + h1 = h1 + a + h2 = h2 + b + h3 = h3 + c + h4 = h4 + d + h5 = h5 + e + h6 = h6 + f + h7 = h7 + g + h8 = h8 + h + end + + local public = {} + + public.asBytes = function() + return { h1, h2, h3, h4, h5, h6, h7, h8} + end + + public.asString = function() + return string.pack(">i8i8i8i8i8i8i8i8", h1, h2, h3, h4, h5, h6, h7, h8) + end + + public.asHex = function() + return Hex.stringToHex(string.pack(">i8i8i8i8i8i8i8i8", h1, h2, h3, h4, h5, h6, h7, h8)) + end + + return public +end + +return sha512 diff --git a/contract/src/crypto/digest/sha3.lua b/contract/src/crypto/digest/sha3.lua new file mode 100644 index 00000000..bef46560 --- /dev/null +++ b/contract/src/crypto/digest/sha3.lua @@ -0,0 +1,235 @@ +local Hex = require(".crypto.util.hex"); + +local ROUNDS = 24 + +local roundConstants = { +0x0000000000000001, +0x0000000000008082, +0x800000000000808A, +0x8000000080008000, +0x000000000000808B, +0x0000000080000001, +0x8000000080008081, +0x8000000000008009, +0x000000000000008A, +0x0000000000000088, +0x0000000080008009, +0x000000008000000A, +0x000000008000808B, +0x800000000000008B, +0x8000000000008089, +0x8000000000008003, +0x8000000000008002, +0x8000000000000080, +0x000000000000800A, +0x800000008000000A, +0x8000000080008081, +0x8000000000008080, +0x0000000080000001, +0x8000000080008008 +} + +local rotationOffsets = { +-- ordered for [x][y] dereferencing, so appear flipped here: +{0, 36, 3, 41, 18}, +{1, 44, 10, 45, 2}, +{62, 6, 43, 15, 61}, +{28, 55, 25, 21, 56}, +{27, 20, 39, 8, 14} +} + + + +-- the full permutation function +local function keccakF(st) + local permuted = st.permuted + local parities = st.parities + for round = 1, ROUNDS do + -- theta() + for x = 1,5 do + parities[x] = 0 + local sx = st[x] + for y = 1,5 do parities[x] = parities[x] ~ sx[y] end + end + -- + -- unroll the following loop + --for x = 1,5 do + -- local p5 = parities[(x)%5 + 1] + -- local flip = parities[(x-2)%5 + 1] ~ ( p5 << 1 | p5 >> 63) + -- for y = 1,5 do st[x][y] = st[x][y] ~ flip end + --end + local p5, flip, s + --x=1 + p5 = parities[2] + flip = parities[5] ~ (p5 << 1 | p5 >> 63) + s = st[1] + for y = 1,5 do s[y] = s[y] ~ flip end + --x=2 + p5 = parities[3] + flip = parities[1] ~ (p5 << 1 | p5 >> 63) + s = st[2] + for y = 1,5 do s[y] = s[y] ~ flip end + --x=3 + p5 = parities[4] + flip = parities[2] ~ (p5 << 1 | p5 >> 63) + s = st[3] + for y = 1,5 do s[y] = s[y] ~ flip end + --x=4 + p5 = parities[5] + flip = parities[3] ~ (p5 << 1 | p5 >> 63) + s = st[4] + for y = 1,5 do s[y] = s[y] ~ flip end + --x=5 + p5 = parities[1] + flip = parities[4] ~ (p5 << 1 | p5 >> 63) + s = st[5] + for y = 1,5 do s[y] = s[y] ~ flip end + + -- rhopi() + for y = 1,5 do + local py = permuted[y] + local r + for x = 1,5 do + s, r = st[x][y], rotationOffsets[x][y] + py[(2*x + 3*y)%5 + 1] = (s << r | s >> (64-r)) + end + end + + -- chi() - unroll the loop + --for x = 1,5 do + -- for y = 1,5 do + -- local combined = (~ permuted[(x)%5 +1][y]) & permuted[(x+1)%5 +1][y] + -- st[x][y] = permuted[x][y] ~ combined + -- end + --end + + local p, p1, p2 + --x=1 + s, p, p1, p2 = st[1], permuted[1], permuted[2], permuted[3] + for y = 1,5 do s[y] = p[y] ~ (~ p1[y]) & p2[y] end + --x=2 + s, p, p1, p2 = st[2], permuted[2], permuted[3], permuted[4] + for y = 1,5 do s[y] = p[y] ~ (~ p1[y]) & p2[y] end + --x=3 + s, p, p1, p2 = st[3], permuted[3], permuted[4], permuted[5] + for y = 1,5 do s[y] = p[y] ~ (~ p1[y]) & p2[y] end + --x=4 + s, p, p1, p2 = st[4], permuted[4], permuted[5], permuted[1] + for y = 1,5 do s[y] = p[y] ~ (~ p1[y]) & p2[y] end + --x=5 + s, p, p1, p2 = st[5], permuted[5], permuted[1], permuted[2] + for y = 1,5 do s[y] = p[y] ~ (~ p1[y]) & p2[y] end + + -- iota() + st[1][1] = st[1][1] ~ roundConstants[round] + end +end + + +local function absorb(st, buffer, algorithm) + + local blockBytes = st.rate / 8 + local blockWords = blockBytes / 8 + + -- append 0x01 byte and pad with zeros to block size (rate/8 bytes) + local totalBytes = #buffer + 1 + -- for keccak (2012 submission), the padding is byte 0x01 followed by zeros + -- for SHA3 (NIST, 2015), the padding is byte 0x06 followed by zeros + + if algorithm == "keccak" then + buffer = buffer .. ( '\x01' .. string.char(0):rep(blockBytes - (totalBytes % blockBytes))) + end + + if algorithm == "sha3" then + buffer = buffer .. ( '\x06' .. string.char(0):rep(blockBytes - (totalBytes % blockBytes))) + end + + totalBytes = #buffer + + --convert data to an array of u64 + local words = {} + for i = 1, totalBytes - (totalBytes % 8), 8 do + words[#words + 1] = string.unpack(' 1) then + out = Array.XOR(out, s); + else + out = s; + end + end + + return out; + end + + public.finish = function() + local blocks = math.ceil(dKeyLen / blockLen); + + dKey = {}; + + for b = 1, blocks do + local block = buildBlock(b); + dKey = Array.concat(dKey, block); + end + + if(Array.size(dKey) > dKeyLen) then dKey = Array.truncate(dKey, dKeyLen); end + + return public; + end + + public.asBytes = function() + return dKey; + end + + public.asHex = function() + return Array.toHex(dKey); + end + + public.asString = function() + return Array.toString(dKey); + end + + return public; +end + +--- @class Array : table + +--- PBKDF2 key derivation function +--- @param password (Array) - The password to derive the key from +--- @param salt (Array) - The salt to use +--- @param iterations number - The number of iterations to perform +--- @param keyLen number - The length of the key to derive +--- @param digest? string - The digest algorithm to use (sha1, sha256). Defaults to sha1. +--- @returns string - The derived key +local pbkdf2 = function(password, salt, iterations, keyLen, digest) + local Digest = nil + if digest == "sha1" then + Digest = SHA1.SHA1 + elseif digest == "sha256" then + Digest = SHA2_256.SHA2_256 + elseif digest == nil then + Digest = SHA1.SHA1 + else + error("Unsupported algorithm: " .. digest) + end + + local prf = HMAC.HMAC().setBlockSize(64).setDigest(Digest); + + local res = PBKDF2() + .setPRF(prf) + .setBlockLen(16) + .setDKeyLen(keyLen) + .setIterations(iterations) + .setSalt(salt) + .setPassword(password) + .finish() + + return res +end + +return { + PBKDF2 = PBKDF2, + pbkdf2 = pbkdf2 +}; \ No newline at end of file diff --git a/contract/src/crypto/mac/hmac.lua b/contract/src/crypto/mac/hmac.lua new file mode 100644 index 00000000..470c4bf9 --- /dev/null +++ b/contract/src/crypto/mac/hmac.lua @@ -0,0 +1,126 @@ +local Bit = require(".crypto.util.bit"); +local Stream = require(".crypto.util.stream"); +local Array = require(".crypto.util.array"); + +local SHA1 = require(".crypto.digest.sha1"); +local SHA2_256 = require(".crypto.digest.sha2_256"); + +local XOR = Bit.bxor; + +local HMAC = function() + local public = {}; + local blockSize = 64; + local Digest = nil; + local outerPadding = {}; + local innerPadding = {} + local digest; + + public.setBlockSize = function(bytes) + blockSize = bytes; + return public; + end + + public.setDigest = function(digestModule) + Digest = digestModule; + digest = Digest(); + return public; + end + + public.setKey = function(key) + local keyStream; + if Digest == nil then + error("Digest not set"); + end + if (Array.size(key) > blockSize) then + keyStream = Stream.fromArray(Digest() + .update(Stream.fromArray(key)) + .finish() + .asBytes()); + else + keyStream = Stream.fromArray(key); + end + + outerPadding = {}; + innerPadding = {}; + + for i = 1, blockSize do + local byte = keyStream(); + if byte == nil then byte = 0x00; end + outerPadding[i] = XOR(0x5C, byte); + innerPadding[i] = XOR(0x36, byte); + end + + return public; + end + + public.init = function() + digest.init() + .update(Stream.fromArray(innerPadding)); + return public; + end + + public.update = function(messageStream) + digest.update(messageStream); + return public; + end + + public.finish = function() + local inner = digest.finish().asBytes(); + digest.init() + .update(Stream.fromArray(outerPadding)) + .update(Stream.fromArray(inner)) + .finish(); + + return public; + end + + public.asBytes = function() + return digest.asBytes(); + end + + public.asHex = function() + return digest.asHex(); + end + + public.asString = function() + return digest.asString(); + end + + return public; +end + +--- @class Array : table +--- @class Stream : table + +--- HMAC function for generating a hash-based message authentication code +--- @param data (Stream) - The data to hash and authenticate +--- @param key (Array) - The key to use for the HMAC +--- @param algorithm? (string) - The algorithm to use for the HMAC (sha1, sha256). Defaults to "sha1" +--- @returns table - A table containing the HMAC in bytes, string, and hex formats. +local hmac = function(data, key, algorithm) + local digest = nil + if algorithm == "sha1" then + digest = SHA1.SHA1 + elseif algorithm == "sha256" then + digest = SHA2_256.SHA2_256 + elseif algorithm == nil then + digest = SHA1.SHA1 + else + error("Unsupported algorithm: " .. algorithm) + end + + local res = HMAC() + .setBlockSize(32) + .setDigest(digest) + .setKey(key) + .init() + .update(data) + .finish() + + return res +end + +return { + hmac = hmac, + HMAC = HMAC +}; \ No newline at end of file diff --git a/contract/src/crypto/mac/init.lua b/contract/src/crypto/mac/init.lua new file mode 100644 index 00000000..65a507d6 --- /dev/null +++ b/contract/src/crypto/mac/init.lua @@ -0,0 +1,8 @@ +local Hmac = require(".crypto.mac.hmac") + +local mac = { + _version = "0.0.1", + createHmac = Hmac.hmac, +}; + +return mac \ No newline at end of file diff --git a/contract/src/crypto/padding/zero.lua b/contract/src/crypto/padding/zero.lua new file mode 100644 index 00000000..0a4f6142 --- /dev/null +++ b/contract/src/crypto/padding/zero.lua @@ -0,0 +1,17 @@ +local ZeroPadding = function(blockSize, byteCount) + + local paddingCount = blockSize - ((byteCount -1) % blockSize) + 1; + local bytesLeft = paddingCount; + + local stream = function() + if bytesLeft > 0 then + bytesLeft = bytesLeft - 1; + return 0x00; + else + return nil; + end + end + return stream; +end + +return ZeroPadding; \ No newline at end of file diff --git a/contract/src/crypto/util/array.lua b/contract/src/crypto/util/array.lua new file mode 100644 index 00000000..4c0be045 --- /dev/null +++ b/contract/src/crypto/util/array.lua @@ -0,0 +1,222 @@ + +local Bit = require(".crypto.util.bit"); +local Queue = require(".crypto.util.queue"); + +local XOR = Bit.bxor; + +local Array = {}; + +Array.size = function(array) + return #array; +end + +Array.fromString = function(string) + local bytes = {}; + + local i = 1; + local byte = string.byte(string, i); + while byte ~= nil do + bytes[i] = byte; + i = i + 1; + byte = string.byte(string, i); + end + + return bytes; + +end + +Array.toString = function(bytes) + local chars = {}; + local i = 1; + + local byte = bytes[i]; + while byte ~= nil do + chars[i] = string.char(byte); + i = i + 1; + byte = bytes[i]; + end + + return table.concat(chars, ""); +end + +Array.fromStream = function(stream) + local array = {}; + local i = 1; + + local byte = stream(); + while byte ~= nil do + array[i] = byte; + i = i + 1; + byte = stream(); + end + + return array; +end + +Array.readFromQueue = function(queue, size) + local array = {}; + + for i = 1, size do + array[i] = queue.pop(); + end + + return array; +end + +Array.writeToQueue = function(queue, array) + local size = Array.size(array); + + for i = 1, size do + queue.push(array[i]); + end +end + +Array.toStream = function(array) + local queue = Queue(); + local i = 1; + + local byte = array[i]; + while byte ~= nil do + queue.push(byte); + i = i + 1; + byte = array[i]; + end + + return queue.pop; +end + + +local fromHexTable = {}; +for i = 0, 255 do + fromHexTable[string.format("%02X", i)] = i; + fromHexTable[string.format("%02x", i)] = i; +end + +Array.fromHex = function(hex) + local array = {}; + + for i = 1, string.len(hex) / 2 do + local h = string.sub(hex, i * 2 - 1, i * 2); + array[i] = fromHexTable[h]; + end + + return array; +end + + +local toHexTable = {}; +for i = 0, 255 do + toHexTable[i] = string.format("%02X", i); +end + +Array.toHex = function(array) + local hex = {}; + local i = 1; + + local byte = array[i]; + while byte ~= nil do + hex[i] = toHexTable[byte]; + i = i + 1; + byte = array[i]; + end + + return table.concat(hex, ""); + +end + +Array.concat = function(a, b) + local concat = {}; + local out = 1; + + local i = 1; + local byte = a[i]; + while byte ~= nil do + concat[out] = byte; + i = i + 1; + out = out + 1; + byte = a[i]; + end + + i = 1; + byte = b[i]; + while byte ~= nil do + concat[out] = byte; + i = i + 1; + out = out + 1; + byte = b[i]; + end + + return concat; +end + +Array.truncate = function(a, newSize) + local x = {}; + + for i = 1, newSize do + x[i] = a[i]; + end + + return x; +end + +Array.XOR = function(a, b) + local x = {}; + + for k, v in pairs(a) do + x[k] = XOR(v, b[k]); + end + + return x; +end + +Array.substitute = function(input, sbox) + local out = {}; + + for k, v in pairs(input) do + out[k] = sbox[v]; + end + + return out; +end + +Array.permute = function(input, pbox) + local out = {}; + + for k, v in pairs(pbox) do + out[k] = input[v]; + end + + return out; +end + +Array.copy = function(input) + local out = {}; + + for k, v in pairs(input) do + out[k] = v; + end + return out; +end + +Array.slice = function(input, start, stop) + local out = {}; + + if start == nil then + start = 1 + elseif start < 0 then + start = #input + start + 1 + end + if stop == nil then + stop = #input + elseif stop < 0 then + stop = #input + stop + 1 + end + + for i = start, stop do + table.insert(out, input[i]) + end + + return out; +end + +return Array; \ No newline at end of file diff --git a/contract/src/crypto/util/bit.lua b/contract/src/crypto/util/bit.lua new file mode 100644 index 00000000..0bbe4484 --- /dev/null +++ b/contract/src/crypto/util/bit.lua @@ -0,0 +1,44 @@ +local ok, e +ok = nil +if not ok then + ok, e = pcall(require, "bit") -- the LuaJIT one ? +end +if not ok then + ok, e = pcall(require, "bit32") -- Lua 5.2 +end +if not ok then + ok, e = pcall(require, "bit.numberlua") -- for Lua 5.1, https://github.com/tst2005/lua-bit-numberlua/ +end +if not ok then + error("no bitwise support found", 2) +end +assert(type(e) == "table", "invalid bit module") + +-- Workaround to support Lua 5.2 bit32 API with the LuaJIT bit one +if e.rol and not e.lrotate then + e.lrotate = e.rol +end +if e.ror and not e.rrotate then + e.rrotate = e.ror +end + +-- Workaround to support incomplete bit operations set +if not e.ror and not e.rrotate then + local ror = function(b, n) + return e.bor(e.rshift(b, n), e.lshift(b, 32 - n)) + end + + e.ror = ror + e.rrotate = ror +end + +if not e.rol and not e.lrotate then + local rol = function(b, n) + return e.bor(e.lshift(b, n), e.rshift(b, 32 - n)) + end + + e.rol = rol + e.lrotate = rol +end + +return e \ No newline at end of file diff --git a/contract/src/crypto/util/hex.lua b/contract/src/crypto/util/hex.lua new file mode 100644 index 00000000..bcc7f2a5 --- /dev/null +++ b/contract/src/crypto/util/hex.lua @@ -0,0 +1,46 @@ + +--- Converts a string to its hexadecimal representation. +--- @param s string The input string. +--- @param ln? number - The number of characters per line. If not provided, the output will be a single line. +--- @param sep? string - The separator between each pair of hexadecimal characters. Defaults to an empty string. +--- @return string The - hexadecimal representation of the input string. +local function stringToHex(s, ln, sep) + if #s == 0 then return "" end + if not ln then + return (s:gsub('.', + function(c) return string.format('%02x', string.byte(c)) end + )) + end + sep = sep or "" + local t = {} + for i = 1, #s - 1 do + t[#t + 1] = string.format("%02x%s", s:byte(i), + (i % ln == 0) and '\n' or sep) + end + t[#t + 1] = string.format("%02x", s:byte(#s)) + return table.concat(t) +end + +--- Converts a hex encoded string to its corresponding decoded string. +--- If the optional parameter `unsafe` is defined, it assumes that the hex string is well-formed +--- (no checks, no whitespace removal). By default, it removes whitespace (including newlines) +--- and checks that the hex string is well-formed. +--- @param hs (string) The hex encoded string to be decoded. +--- @param unsafe (boolean) [optional] If true, assumes the hex string is well-formed. +--- @return (string) The decoded string. +local function hexToString(hs, unsafe) + local tonumber = tonumber + if not unsafe then + hs = string.gsub(hs, "%s+", "") -- remove whitespaces + if string.find(hs, '[^0-9A-Za-z]') or #hs % 2 ~= 0 then + error("invalid hex string") + end + end + local count = string.gsub(hs, '(%x%x)',function(c) return string.char(tonumber(c, 16)) end) + return count +end + +return { + stringToHex = stringToHex, + hexToString = hexToString, +} \ No newline at end of file diff --git a/contract/src/crypto/util/init.lua b/contract/src/crypto/util/init.lua new file mode 100644 index 00000000..80afdda6 --- /dev/null +++ b/contract/src/crypto/util/init.lua @@ -0,0 +1,16 @@ +local Bit = require(".crypto.util.bit") +local Queue = require(".crypto.util.queue") +local Stream = require(".crypto.util.stream") +local Hex = require(".crypto.util.hex") +local Array = require(".crypto.util.array") + +local util = { + _version = "0.0.1", + bit = Bit, + queue = Queue, + stream = Stream, + hex = Hex, + array = Array, +} + +return util diff --git a/contract/src/crypto/util/queue.lua b/contract/src/crypto/util/queue.lua new file mode 100644 index 00000000..48181929 --- /dev/null +++ b/contract/src/crypto/util/queue.lua @@ -0,0 +1,47 @@ +local Queue = function() + local queue = {}; + local tail = 0; + local head = 0; + + local public = {}; + + public.push = function(obj) + queue[head] = obj; + head = head + 1; + return; + end + + public.pop = function() + if tail < head + then + local obj = queue[tail]; + queue[tail] = nil; + tail = tail + 1; + return obj; + else + return nil; + end + end + + public.size = function() + return head - tail; + end + + public.getHead = function() + return head; + end + + public.getTail = function() + return tail; + end + + public.reset = function() + queue = {}; + head = 0; + tail = 0; + end + + return public; +end + +return Queue; \ No newline at end of file diff --git a/contract/src/crypto/util/stream.lua b/contract/src/crypto/util/stream.lua new file mode 100644 index 00000000..49d52f52 --- /dev/null +++ b/contract/src/crypto/util/stream.lua @@ -0,0 +1,98 @@ +local Queue = require(".crypto.util.queue"); + +local Stream = {}; + + +Stream.fromString = function(string) + local i = 0; + return function() + i = i + 1; + return string.byte(string, i); + end +end + + +Stream.toString = function(stream) + local array = {}; + local i = 1; + + local byte = stream(); + while byte ~= nil do + array[i] = string.char(byte); + i = i + 1; + byte = stream(); + end + + return table.concat(array); +end + + +Stream.fromArray = function(array) + local queue = Queue(); + local i = 1; + + local byte = array[i]; + while byte ~= nil do + queue.push(byte); + i = i + 1; + byte = array[i]; + end + + return queue.pop; +end + + +Stream.toArray = function(stream) + local array = {}; + local i = 1; + + local byte = stream(); + while byte ~= nil do + array[i] = byte; + i = i + 1; + byte = stream(); + end + + return array; +end + + +local fromHexTable = {}; +for i = 0, 255 do + fromHexTable[string.format("%02X", i)] = i; + fromHexTable[string.format("%02x", i)] = i; +end + +Stream.fromHex = function(hex) + local queue = Queue(); + + for i = 1, string.len(hex) / 2 do + local h = string.sub(hex, i * 2 - 1, i * 2); + queue.push(fromHexTable[h]); + end + + return queue.pop; +end + + + +local toHexTable = {}; +for i = 0, 255 do + toHexTable[i] = string.format("%02X", i); +end + +Stream.toHex = function(stream) + local hex = {}; + local i = 1; + + local byte = stream(); + while byte ~= nil do + hex[i] = toHexTable[byte]; + i = i + 1; + byte = stream(); + end + + return table.concat(hex); +end + +return Stream; \ No newline at end of file diff --git a/contract/src/gar.lua b/contract/src/gar.lua index 9454084f..bb97f825 100644 --- a/contract/src/gar.lua +++ b/contract/src/gar.lua @@ -1,5 +1,6 @@ -- gar.lua require("state") +local crypto = require('.crypto') local utils = require("utils") local constants = require("constants") local gar = {} @@ -333,9 +334,72 @@ function gar.saveObservations() utils.reply("saveObservations is not implemented yet") end -function gar.getPrescribedObservers() - -- TODO: implement - utils.reply("getPrescribedObservers is not implemented yet") +function gar.getEpochDataForTimestamp(currentTimestamp) + local epochIndexForCurrentTimestamp = math.floor( + math.max( + 0, + (currentTimestamp - constants.epochZeroStartTimestamp) / constants.epochTimeLength + ) + ) + + local epochStartTimestamp = constants.epochZeroStartTimestamp + + constants.epochTimeLength * epochIndexForCurrentTimestamp + local epochEndTimestamp = epochStartTimestamp + constants.epochTimeLength + local epochDistributionTimestamp = epochEndTimestamp + constants.EPOCH_DISTRIBUTION_DELAY + return epochStartTimestamp, epochEndTimestamp, epochDistributionTimestamp, epochIndexForCurrentTimestamp +end + +function gar.getPrescribedObservers(currentTimestamp) + local epochStartTimestamp, epochEndTimestamp, epochDistributionTimestamp, epochIndexForCurrentTimestamp = gar + .getEpochDataForTimestamp(currentTimestamp) + + local existingOrComputedObservers = PrescribedObservers[epochIndexForCurrentTimestamp] or {} + return existingOrComputedObservers +end + +function gar.getPrescribedObserversForEpoch(epochStartTimestamp, epochEndTimestamp, hashchain) + local eligibleGateways = utils.getEligibleGatewaysForEpoch(epochStartTimestamp, epochEndTimestamp) + local weightedObservers = utils.getObserverWeightsForEpoch(epochStartTimestamp) + -- Filter out any observers that could have a normalized composite weight of 0 + local filteredObservers = {} + for _, observer in ipairs(weightedObservers) do + if observer.normalizedCompositeWeight > 0 then + table.insert(filteredObservers, observer) + end + end + + weightedObservers = filteredObservers + if constants.MAXIMUM_OBSERVERS_PER_EPOCH >= weightedObservers then + return weightedObservers + end + + local timestampEntropyHash = utils.getEntropyHashForEpoch(hashchain) + local prescribedObserversAddresses = {} + local hash = timestampEntropyHash + while (utils.tableLength(prescribedObserversAddresses) < constants.MAXIMUM_OBSERVERS_PER_EPOCH) do + --local random = readUInt32BE(hash) / 0xffffffff -- Convert hash to a value between 0 and 1 + local random = 1 + local cumulativeNormalizedCompositeWeight = 0 + + for _, observer in ipairs(weightedObservers) do + -- skip observers that have already been prescribed + if prescribedObserversAddresses[observer.gatewayAddress] then + goto continue + end + -- add the observers normalized composite weight to the cumulative weight + cumulativeNormalizedCompositeWeight = cumulativeNormalizedCompositeWeight + + observer.normalizedCompositeWeight + -- if the random value is less than the cumulative weight, we have found our observer + if random <= cumulativeNormalizedCompositeWeight then + prescribedObserversAddresses[observer.gatewayAddress] = true + break + end + ::continue:: + end + -- Compute the next hash for the next iteration + -- hash = hashFunction(hash) -- Assuming hashFunction is synchronous and already defined + hash = 1 + end end function gar.getEpoch() diff --git a/contract/src/state.lua b/contract/src/state.lua index 395896f8..2e2e78bd 100644 --- a/contract/src/state.lua +++ b/contract/src/state.lua @@ -18,4 +18,8 @@ end if not Vaults then Vaults = {} end + +if not PrescribedObservers then + PrescribedObservers = {} +end return state diff --git a/contract/src/utils.lua b/contract/src/utils.lua index b1bd16d7..2f639de7 100644 --- a/contract/src/utils.lua +++ b/contract/src/utils.lua @@ -1,5 +1,6 @@ local constants = require("constants") local utils = {} +local crypto = require('.crypto') function utils.hasMatchingTag(tag, value) return Handlers.utils.hasMatchingTag(tag, value) @@ -417,16 +418,106 @@ function utils.calculateYearsBetweenTimestamps(startTimestamp, endTimestamp) return yearsRemainingFloat end +function utils.isGatewayLeaving(gateway, currentTimestamp) + return gateway.status == 'leaving' and gateway.endTimestamp <= currentTimestamp +end + function utils.isGatewayEligibleToLeave(gateway, - currentTimestamp) + timestamp) if gateway == nil then print('gateway is nil') return false end - local isJoined = utils.isGatewayJoined(gateway, currentTimestamp); + local isJoined = utils.isGatewayJoined(gateway, timestamp); return isJoined; end +function utils.isGatewayEligibleForDistribution(epochStartTimestamp, epochEndTimestamp, gateway) + local didStartBeforeEpoch = gateway.startTimestamp <= epochStartTimestamp + local didNotLeaveDuringEpoch = not utils.isGatewayLeaving(gateway, epochEndTimestamp) + return didStartBeforeEpoch and didNotLeaveDuringEpoch +end + +function utils.getEligibleGatewaysForEpoch(epochStartTimestamp, epochEndTimestamp) + local eligibleGateways = {} + for address, gateway in pairs(Gateways) do + if utils.isGatewayEligibleForDistribution( + epochStartTimestamp, + epochEndTimestamp, + gateway + ) then + eligibleGateways[address] = gateway + end + end + return eligibleGateways +end + +function utils.getObserverWeightsForEpoch(epochStartTimestamp) + local weightedObservers = {} + local totalCompositeWeight = 0 + + -- Iterate over gateways to calculate weights + for address, gateway in pairs(Gateways) do + local totalStake = gateway.operatorStake + gateway.totalDelegatedStake -- 100 - no cap to this + local stakeWeightRatio = totalStake / + constants + .MIN_OPERATOR_STAKE -- this is always greater than 1 as the minOperatorStake is always less than the stake + -- the percentage of the epoch the gateway was joined for before this epoch, if the gateway starts in the future this will be 0 + local gatewayStartTimestamp = gateway.startTimestamp + local totalTimeForGateway = epochStartTimestamp >= gatewayStartTimestamp and + (epochStartTimestamp - gatewayStartTimestamp) or -1 + -- TODO: should we increment by one here or are observers that join at the epoch start not eligible to be selected as an observer + + + + local calculatedTenureWeightForGateway = totalTimeForGateway < 0 and 0 or + (totalTimeForGateway > 0 and totalTimeForGateway / constants.TENURE_WEIGHT_PERIOD or 1 / constants.TENURE_WEIGHT_PERIOD) + local gatewayTenureWeight = math.min(calculatedTenureWeightForGateway, constants.MAX_TENURE_WEIGHT) + + local totalEpochsGatewayPassed = gateway.stats.passedEpochCount or 0 + local totalEpochsParticipatedIn = gateway.stats.totalEpochParticipationCount or 0 + local gatewayRewardRatioWeight = (1 + totalEpochsGatewayPassed) / (1 + totalEpochsParticipatedIn) + + local totalEpochsPrescribed = gateway.stats.totalEpochsPrescribedCount or 0 + local totalEpochsSubmitted = gateway.stats.submittedEpochCount or 0 + local observerRewardRatioWeight = (1 + totalEpochsSubmitted) / (1 + totalEpochsPrescribed) + + local compositeWeight = stakeWeightRatio * gatewayTenureWeight * gatewayRewardRatioWeight * + observerRewardRatioWeight + + table.insert(weightedObservers, { + gatewayAddress = address, + observerAddress = gateway.observerWallet, + stake = totalStake, + startTimestamp = gateway.startTimestamp, + stakeWeight = stakeWeightRatio, + tenureWeight = gatewayTenureWeight, + gatewayRewardRatioWeight = gatewayRewardRatioWeight, + observerRewardRatioWeight = observerRewardRatioWeight, + compositeWeight = compositeWeight, + normalizedCompositeWeight = nil -- set later once we have the total composite weight + }) + + totalCompositeWeight = totalCompositeWeight + compositeWeight + end + + -- Calculate the normalized composite weight for each observer + for _, weightedObserver in ipairs(weightedObservers) do + if totalCompositeWeight > 0 then + weightedObserver.normalizedCompositeWeight = weightedObserver.compositeWeight / totalCompositeWeight + else + weightedObserver.normalizedCompositeWeight = 0 + end + end + return weightedObservers +end + +function utils.getEntropyHashForEpoch(hashChain) + local bufferHash = Buffer.from('') + bufferHash = Buffer.concat([bufferHash, Buffer.from(hashChain, 'base64url')]) + return crypto.hash(bufferHash, 'SHA-256') +end + function utils.isGatewayJoined(gateway, currentTimestamp) return gateway.status == 'joined' and gateway.startTimestamp <= currentTimestamp @@ -445,4 +536,10 @@ function utils.printTable(tbl, indent) end end +function utils.tableLength(T) + local count = 0 + for _ in pairs(T) do count = count + 1 end + return count +end + return utils