diff --git a/lockbox/LICENSE b/lockbox/LICENSE new file mode 100644 index 0000000..9fed1ed --- /dev/null +++ b/lockbox/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 James L. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/lockbox/cipher/aes128.lua b/lockbox/cipher/aes128.lua new file mode 100644 index 0000000..0726ac4 --- /dev/null +++ b/lockbox/cipher/aes128.lua @@ -0,0 +1,415 @@ +local Array = require("lockbox.util.array"); +local Bit = require("lockbox.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 key = keyExpand(_key); + + --round 0 + block = addKey(block, key[1]); + + --round 1 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[2]); + + --round 2 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[3]); + + --round 3 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[4]); + + --round 4 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[5]); + + --round 5 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[6]); + + --round 6 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[7]); + + --round 7 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[8]); + + --round 8 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[9]); + + --round 9 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[10]); + + --round 10 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = addKey(block, key[11]); + + return block; + +end + +AES.decrypt = function(_key, block) + + local key = keyExpand(_key); + + --round 0 + block = addKey(block, key[11]); + + --round 1 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[10]); + block = mixCol(block, IMIXTABLE); + + --round 2 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[9]); + block = mixCol(block, IMIXTABLE); + + --round 3 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[8]); + block = mixCol(block, IMIXTABLE); + + --round 4 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[7]); + block = mixCol(block, IMIXTABLE); + + --round 5 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[6]); + block = mixCol(block, IMIXTABLE); + + --round 6 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[5]); + block = mixCol(block, IMIXTABLE); + + --round 7 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[4]); + block = mixCol(block, IMIXTABLE); + + --round 8 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[3]); + block = mixCol(block, IMIXTABLE); + + --round 9 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[2]); + block = mixCol(block, IMIXTABLE); + + --round 10 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[1]); + + return block; +end + +return AES; diff --git a/lockbox/cipher/aes192.lua b/lockbox/cipher/aes192.lua new file mode 100644 index 0000000..5f55b0e --- /dev/null +++ b/lockbox/cipher/aes192.lua @@ -0,0 +1,462 @@ + +local Array = require("lockbox.util.array"); +local Bit = require("lockbox.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 key = keyExpand(_key); + + --round 0 + block = addKey(block, key[1]); + + --round 1 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[2]); + + --round 2 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[3]); + + --round 3 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[4]); + + --round 4 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[5]); + + --round 5 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[6]); + + --round 6 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[7]); + + --round 7 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[8]); + + --round 8 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[9]); + + --round 9 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[10]); + + --round 10 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[11]); + + --round 11 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[12]); + + --round 12 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = addKey(block, key[13]); + + return block; + +end + +AES.decrypt = function(_key, block) + + local key = keyExpand(_key); + + --round 0 + block = addKey(block, key[13]); + + --round 1 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[12]); + block = mixCol(block, IMIXTABLE); + + --round 2 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[11]); + block = mixCol(block, IMIXTABLE); + + --round 3 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[10]); + block = mixCol(block, IMIXTABLE); + + --round 4 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[9]); + block = mixCol(block, IMIXTABLE); + + --round 5 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[8]); + block = mixCol(block, IMIXTABLE); + + --round 6 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[7]); + block = mixCol(block, IMIXTABLE); + + --round 7 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[6]); + block = mixCol(block, IMIXTABLE); + + --round 8 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[5]); + block = mixCol(block, IMIXTABLE); + + --round 9 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[4]); + block = mixCol(block, IMIXTABLE); + + --round 10 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[3]); + block = mixCol(block, IMIXTABLE); + + --round 11 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[2]); + block = mixCol(block, IMIXTABLE); + + --round 12 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[1]); + + return block; +end + +return AES; diff --git a/lockbox/cipher/aes256.lua b/lockbox/cipher/aes256.lua new file mode 100644 index 0000000..854bae9 --- /dev/null +++ b/lockbox/cipher/aes256.lua @@ -0,0 +1,498 @@ +local Array = require("lockbox.util.array"); +local Bit = require("lockbox.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 key = keyExpand(_key); + + --round 0 + block = addKey(block, key[1]); + + --round 1 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[2]); + + --round 2 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[3]); + + --round 3 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[4]); + + --round 4 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[5]); + + --round 5 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[6]); + + --round 6 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[7]); + + --round 7 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[8]); + + --round 8 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[9]); + + --round 9 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[10]); + + --round 10 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[11]); + + --round 11 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[12]); + + --round 12 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[13]); + + --round 13 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = mixCol(block, MIXTABLE); + block = addKey(block, key[14]); + + --round 14 + block = byteSub(block, SBOX); + block = shiftRow(block, ROW_SHIFT); + block = addKey(block, key[15]); + + return block; + +end + +AES.decrypt = function(_key, block) + + local key = keyExpand(_key); + + --round 0 + block = addKey(block, key[15]); + + --round 1 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[14]); + block = mixCol(block, IMIXTABLE); + + --round 2 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[13]); + block = mixCol(block, IMIXTABLE); + + --round 3 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[12]); + block = mixCol(block, IMIXTABLE); + + --round 4 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[11]); + block = mixCol(block, IMIXTABLE); + + --round 5 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[10]); + block = mixCol(block, IMIXTABLE); + + --round 6 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[9]); + block = mixCol(block, IMIXTABLE); + + --round 7 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[8]); + block = mixCol(block, IMIXTABLE); + + --round 8 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[7]); + block = mixCol(block, IMIXTABLE); + + --round 9 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[6]); + block = mixCol(block, IMIXTABLE); + + --round 10 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[5]); + block = mixCol(block, IMIXTABLE); + + --round 11 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[4]); + block = mixCol(block, IMIXTABLE); + + --round 12 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[3]); + block = mixCol(block, IMIXTABLE); + + --round 13 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[2]); + block = mixCol(block, IMIXTABLE); + + --round 14 + block = shiftRow(block, IROW_SHIFT); + block = byteSub(block, ISBOX); + block = addKey(block, key[1]); + + return block; +end + +return AES; diff --git a/lockbox/cipher/mode/cbc.lua b/lockbox/cipher/mode/cbc.lua new file mode 100644 index 0000000..a02ff2e --- /dev/null +++ b/lockbox/cipher/mode/cbc.lua @@ -0,0 +1,164 @@ +local Array = require("lockbox.util.array"); +local Stream = require("lockbox.util.stream"); +local Queue = require("lockbox.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 + + 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 + + return public; + +end + +return CBC; + diff --git a/lockbox/cipher/mode/cfb.lua b/lockbox/cipher/mode/cfb.lua new file mode 100644 index 0000000..c736d52 --- /dev/null +++ b/lockbox/cipher/mode/cfb.lua @@ -0,0 +1,163 @@ +local Array = require("lockbox.util.array"); +local Stream = require("lockbox.util.stream"); +local Queue = require("lockbox.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 + + 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 + + return public; + +end + +return CFB; diff --git a/lockbox/cipher/mode/ctr.lua b/lockbox/cipher/mode/ctr.lua new file mode 100644 index 0000000..beb8ef0 --- /dev/null +++ b/lockbox/cipher/mode/ctr.lua @@ -0,0 +1,248 @@ +local Array = require("lockbox.util.array"); +local Stream = require("lockbox.util.stream"); +local Queue = require("lockbox.util.queue"); + +local Bit = require("lockbox.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 + + 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 + + return public; + +end + + + + +return CTR; + diff --git a/lockbox/cipher/mode/ofb.lua b/lockbox/cipher/mode/ofb.lua new file mode 100644 index 0000000..a824846 --- /dev/null +++ b/lockbox/cipher/mode/ofb.lua @@ -0,0 +1,164 @@ +local Array = require("lockbox.util.array"); +local Stream = require("lockbox.util.stream"); +local Queue = require("lockbox.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 + + 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 + + return public; + +end + + +return OFB; diff --git a/lockbox/digest/sha1.lua b/lockbox/digest/sha1.lua new file mode 100644 index 0000000..fc38866 --- /dev/null +++ b/lockbox/digest/sha1.lua @@ -0,0 +1,173 @@ +require("lockbox").insecure(); + +local Bit = require("lockbox.util.bit"); +local String = require("string"); +local Math = require("math"); +local Queue = require("lockbox.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 + + return public; +end + +return SHA1; diff --git a/lockbox/digest/sha2_224.lua b/lockbox/digest/sha2_224.lua new file mode 100644 index 0000000..3bb536a --- /dev/null +++ b/lockbox/digest/sha2_224.lua @@ -0,0 +1,200 @@ +local Bit = require("lockbox.util.bit"); +local String = require("string"); +local Math = require("math"); +local Queue = require("lockbox.util.queue"); + +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" + +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_224 = function() + + local queue = Queue(); + + local h0 = 0xc1059ed8; + local h1 = 0x367cd507; + local h2 = 0x3070dd17; + local h3 = 0xf70e5939; + local h4 = 0xffc00b31; + local h5 = 0x68581511; + local h6 = 0x64f98fa7; + local h7 = 0xbefa4fa4; + + 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 = 0xc1059ed8; + h1 = 0x367cd507; + h2 = 0x3070dd17; + h3 = 0xf70e5939; + h4 = 0xffc00b31; + h5 = 0x68581511; + h6 = 0x64f98fa7; + h7 = 0xbefa4fa4; + + 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); + + 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}; + 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); + + 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); + end + + return public; + +end + +return SHA2_224; + diff --git a/lockbox/digest/sha2_256.lua b/lockbox/digest/sha2_256.lua new file mode 100644 index 0000000..1aafa5a --- /dev/null +++ b/lockbox/digest/sha2_256.lua @@ -0,0 +1,203 @@ +local Bit = require("lockbox.util.bit"); +local String = require("string"); +local Math = require("math"); +local Queue = require("lockbox.util.queue"); + +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 + + return public; + +end + +return SHA2_256; + diff --git a/lockbox/init.lua b/lockbox/init.lua new file mode 100644 index 0000000..0031a50 --- /dev/null +++ b/lockbox/init.lua @@ -0,0 +1,22 @@ +local Lockbox = {}; + +--[[ +package.path = "./?.lua;" + .. "./cipher/?.lua;" + .. "./digest/?.lua;" + .. "./kdf/?.lua;" + .. "./mac/?.lua;" + .. "./padding/?.lua;" + .. "./test/?.lua;" + .. "./util/?.lua;" + .. package.path; +--]] +Lockbox.ALLOW_INSECURE = true; + +Lockbox.insecure = function() + assert(Lockbox.ALLOW_INSECURE, + "This module is insecure! It should not be used in production." .. + "If you really want to use it, set Lockbox.ALLOW_INSECURE to true before importing it"); +end + +return Lockbox; diff --git a/lockbox/kdf/pbkdf2.lua b/lockbox/kdf/pbkdf2.lua new file mode 100644 index 0000000..a05e42a --- /dev/null +++ b/lockbox/kdf/pbkdf2.lua @@ -0,0 +1,114 @@ +local Bit = require("lockbox.util.bit"); +local Array = require("lockbox.util.array"); +local Stream = require("lockbox.util.stream"); +local Math = require("math"); + +local AND = Bit.band; +local RSHIFT = Bit.rshift; + +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 PBKDF2 = function() + + local public = {}; + + local blockLen = 16; + local dKeyLen = 256; + local iterations = 4096; + + local salt; + local password; + + + local PRF; + + local dKey; + + + public.setBlockLen = function(len) + blockLen = len; + return public; + end + + public.setDKeyLen = function(len) + dKeyLen = len + return public; + end + + public.setIterations = function(iter) + iterations = iter; + return public; + end + + public.setSalt = function(saltBytes) + salt = saltBytes; + return public; + end + + public.setPassword = function(passwordBytes) + password = passwordBytes; + return public; + end + + public.setPRF = function(prf) + PRF = prf; + return public; + end + + local buildBlock = function(i) + local b0, b1, b2, b3 = word2bytes(i); + local ii = {b0, b1, b2, b3}; + local s = Array.concat(salt, ii); + + local out = {}; + + PRF.setKey(password); + for c = 1, iterations do + PRF.init() + .update(Stream.fromArray(s)); + + s = PRF.finish().asBytes(); + if(c > 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 + + return public; +end + +return PBKDF2; diff --git a/lockbox/mac/hmac.lua b/lockbox/mac/hmac.lua new file mode 100644 index 0000000..a10b84c --- /dev/null +++ b/lockbox/mac/hmac.lua @@ -0,0 +1,85 @@ +local Bit = require("lockbox.util.bit"); +local Stream = require("lockbox.util.stream"); +local Array = require("lockbox.util.array"); + +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(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 + + return public; + +end + +return HMAC; diff --git a/lockbox/padding/ansix923.lua b/lockbox/padding/ansix923.lua new file mode 100644 index 0000000..83702c6 --- /dev/null +++ b/lockbox/padding/ansix923.lua @@ -0,0 +1,22 @@ +local ANSIX923Padding = function(blockSize, byteCount) + + local paddingCount = blockSize - (byteCount % blockSize); + local bytesLeft = paddingCount; + + local stream = function() + if bytesLeft > 1 then + bytesLeft = bytesLeft - 1; + return 0x00; + elseif bytesLeft > 0 then + bytesLeft = bytesLeft - 1; + return paddingCount; + else + return nil; + end + end + + return stream; + +end + +return ANSIX923Padding; diff --git a/lockbox/padding/isoiec7816.lua b/lockbox/padding/isoiec7816.lua new file mode 100644 index 0000000..3dc255d --- /dev/null +++ b/lockbox/padding/isoiec7816.lua @@ -0,0 +1,22 @@ +local ISOIEC7816Padding = function(blockSize, byteCount) + + local paddingCount = blockSize - (byteCount % blockSize); + local bytesLeft = paddingCount; + + local stream = function() + if bytesLeft == paddingCount then + bytesLeft = bytesLeft - 1; + return 0x80; + elseif bytesLeft > 0 then + bytesLeft = bytesLeft - 1; + return 0x00; + else + return nil; + end + end + + return stream; + +end + +return ISOIEC7816Padding; diff --git a/lockbox/padding/pkcs7.lua b/lockbox/padding/pkcs7.lua new file mode 100644 index 0000000..3b635ab --- /dev/null +++ b/lockbox/padding/pkcs7.lua @@ -0,0 +1,18 @@ +local PKCS7Padding = function(blockSize, byteCount) + + local paddingCount = blockSize - ((byteCount -1) % blockSize) + 1; + local bytesLeft = paddingCount; + + local stream = function() + if bytesLeft > 0 then + bytesLeft = bytesLeft - 1; + return paddingCount; + else + return nil; + end + end + + return stream; +end + +return PKCS7Padding; diff --git a/lockbox/padding/zero.lua b/lockbox/padding/zero.lua new file mode 100644 index 0000000..d42a9b7 --- /dev/null +++ b/lockbox/padding/zero.lua @@ -0,0 +1,19 @@ +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; diff --git a/lockbox/util/array.lua b/lockbox/util/array.lua new file mode 100644 index 0000000..bd9ed56 --- /dev/null +++ b/lockbox/util/array.lua @@ -0,0 +1,211 @@ + +local String = require("string"); +local Bit = require("lockbox.util.bit"); +local Queue = require("lockbox.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 = {}; + + for i = start, stop do + out[i - start + 1] = input[i]; + end + return out; +end + +return Array; diff --git a/lockbox/util/bit.lua b/lockbox/util/bit.lua new file mode 100644 index 0000000..b17238e --- /dev/null +++ b/lockbox/util/bit.lua @@ -0,0 +1,25 @@ +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 + +return e diff --git a/lockbox/util/queue.lua b/lockbox/util/queue.lua new file mode 100644 index 0000000..4a4a345 --- /dev/null +++ b/lockbox/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; diff --git a/lockbox/util/stream.lua b/lockbox/util/stream.lua new file mode 100644 index 0000000..f81a18c --- /dev/null +++ b/lockbox/util/stream.lua @@ -0,0 +1,99 @@ +local Queue = require("lockbox.util.queue"); +local String = require("string"); + +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; diff --git a/scada-common/crypto.lua b/scada-common/crypto.lua new file mode 100644 index 0000000..19db76b --- /dev/null +++ b/scada-common/crypto.lua @@ -0,0 +1,246 @@ +local aes128 = require("lockbox.cipher.aes128") +local ctr_mode = require("lockbox.cipher.mode.ctr"); + +local sha1 = require("lockbox.digest.sha1"); +local sha2_224 = require("lockbox.digest.sha2_224"); +local sha2_256 = require("lockbox.digest.sha2_256"); + +local pbkdf2 = require("lockbox.kdf.pbkdf2") + +local hmac = require("lockbox.mac.hmac") + +local zero_pad = require("lockbox.padding.zero"); + +local stream = require("lockbox.util.stream") +local array = require("lockbox.util.array") + +local log = require("scada-common.log") +local util = require("scada-common.util") + +local crypto = {} + +local c_eng = { + key = nil, + cipher = nil, + decipher = nil, + hmac = nil +} + +---@alias hex string + +-- initialize cryptographic system +function crypto.init(password, server_port) + local key_deriv = pbkdf2() + + -- setup PBKDF2 + -- the primary goal is to just turn our password into a 16 byte key + key_deriv.setPassword(password) + key_deriv.setSalt("salty_salt_at_" .. server_port) + key_deriv.setIterations(32) + key_deriv.setBlockLen(8) + key_deriv.setDKeyLen(16) + + local start = util.time() + + key_deriv.setPRF(hmac().setBlockSize(64).setDigest(sha2_256)) + key_deriv.finish() + + log.dmesg("pbkdf2: key derivation took " .. (util.time() - start) .. "ms", "CRYPTO", colors.yellow) + + c_eng.key = array.fromHex(key_deriv.asHex()) + + -- initialize cipher + c_eng.cipher = ctr_mode.Cipher() + c_eng.cipher.setKey(c_eng.key) + c_eng.cipher.setBlockCipher(aes128) + c_eng.cipher.setPadding(zero_pad); + + -- initialize decipher + c_eng.decipher = ctr_mode.Decipher() + c_eng.decipher.setKey(c_eng.key) + c_eng.decipher.setBlockCipher(aes128) + c_eng.decipher.setPadding(zero_pad); + + -- initialize HMAC + c_eng.hmac = hmac() + c_eng.hmac.setBlockSize(64) + c_eng.hmac.setDigest(sha1) + c_eng.hmac.setKey(c_eng.key) + + log.dmesg("init: completed in " .. (util.time() - start) .. "ms", "CRYPTO", colors.yellow) +end + +-- encrypt plaintext +---@param plaintext string +---@return string initial_value, string ciphertext +function crypto.encrypt(plaintext) + local start = util.time() + + -- initial value + local iv = { + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255), + math.random(0, 255) + } + + log.debug("crypto.random: iv random took " .. (util.time() - start) .. "ms") + + start = util.time() + + c_eng.cipher.init() + c_eng.cipher.update(stream.fromArray(iv)) + c_eng.cipher.update(stream.fromString(plaintext)) + c_eng.cipher.finish() + + local ciphertext = c_eng.cipher.asHex() ---@type hex + + log.debug("crypto.encrypt: aes128-ctr-mode took " .. (util.time() - start) .. "ms") + log.debug("ciphertext: " .. ciphertext) + + return iv, ciphertext +end + +-- decrypt ciphertext +---@param iv string CTR initial value +---@param ciphertext string ciphertext hex +---@return string plaintext +function crypto.decrypt(iv, ciphertext) + local start = util.time() + + c_eng.decipher.init() + c_eng.decipher.update(stream.fromArray(iv)) + c_eng.decipher.update(stream.fromHex(ciphertext)) + c_eng.decipher.finish() + + local plaintext_hex = c_eng.decipher.asHex() ---@type hex + + local plaintext = stream.toString(stream.fromHex(plaintext_hex)) + + log.debug("crypto.decrypt: aes128-ctr-mode took " .. (util.time() - start) .. "ms") + log.debug("plaintext: " .. plaintext) + + return plaintext +end + +-- generate HMAC of message +---@param message_hex string initial value concatenated with ciphertext +function crypto.hmac(message_hex) + local start = util.time() + + c_eng.hmac.init() + c_eng.hmac.update(stream.fromHex(message_hex)) + c_eng.hmac.finish() + + local hash = c_eng.hmac.asHex() ---@type hex + + log.debug("crypto.hmac: hmac-sha1 took " .. (util.time() - start) .. "ms") + log.debug("hmac: " .. hash) + + return hash +end + +-- wrap a modem as a secure modem to send encrypted traffic +---@param modem table modem to wrap +function crypto.secure_modem(modem) + local self = { + modem = modem + } + + ---@class secure_modem + ---@field open function + ---@field isOpen function + ---@field close function + ---@field closeAll function + ---@field isWireless function + ---@field getNamesRemote function + ---@field isPresentRemote function + ---@field getTypeRemote function + ---@field hasTypeRemote function + ---@field getMethodsRemote function + ---@field callRemote function + ---@field getNameLocal function + local public = {} + + -- wrap a modem + ---@param modem table +---@diagnostic disable-next-line: redefined-local + function public.wrap(modem) + self.modem = modem + for key, func in pairs(self.modem) do + public[key] = func + end + end + + -- wrap modem functions, then we replace transmit + public.wrap(self.modem) + + -- send a packet with encryption + ---@param channel integer + ---@param reply_channel integer + ---@param payload table packet raw_sendable + function public.transmit(channel, reply_channel, payload) + local plaintext = textutils.serialize(payload, { allow_repetitions = true, compact = true }) + + local iv, ciphertext = crypto.encrypt(plaintext) +---@diagnostic disable-next-line: redefined-local + local hmac = crypto.hmac(iv .. ciphertext) + + self.modem.transmit(channel, reply_channel, { hmac, iv, ciphertext }) + end + + -- parse in a modem message as a network packet + ---@param side string + ---@param sender integer + ---@param reply_to integer + ---@param message any encrypted packet sent with secure_modem.transmit + ---@param distance integer + ---@return string side, integer sender, integer reply_to, any plaintext_message, integer distance + function public.receive(side, sender, reply_to, message, distance) + local body = "" + + if type(message) == "table" then + if #message == 3 then +---@diagnostic disable-next-line: redefined-local + local hmac = message[1] + local iv = message[2] + local ciphertext = message[3] + + local computed_hmac = crypto.hmac(iv .. ciphertext) + + if hmac == computed_hmac then + -- message intact + local plaintext = crypto.decrypt(iv, ciphertext) + body = textutils.deserialize(plaintext) + + if body == nil then + -- failed decryption + log.debug("crypto.secure_modem: decryption failed") + body = "" + end + else + -- something went wrong + log.debug("crypto.secure_modem: hmac mismatch violation") + end + end + end + + return side, sender, reply_to, body, distance + end + + return public +end + +return crypto diff --git a/test/lockbox-benchmark.lua b/test/lockbox-benchmark.lua new file mode 100644 index 0000000..f6f1ec2 --- /dev/null +++ b/test/lockbox-benchmark.lua @@ -0,0 +1,104 @@ +require("/initenv").init_env() + +local pbkdf2 = require("lockbox.kdf.pbkdf2") +local AES128Cipher = require("lockbox.cipher.aes128") +local HMAC = require("lockbox.mac.hmac") +local SHA1 = require("lockbox.digest.sha1"); +local SHA2_224 = require("lockbox.digest.sha2_224"); +local SHA2_256 = require("lockbox.digest.sha2_256"); +local Stream = require("lockbox.util.stream") +local Array = require("lockbox.util.array") + +local CBCMode = require("lockbox.cipher.mode.cbc"); +local CFBMode = require("lockbox.cipher.mode.cfb"); +local OFBMode = require("lockbox.cipher.mode.ofb"); +local CTRMode = require("lockbox.cipher.mode.ctr"); + +local ZeroPadding = require("lockbox.padding.zero"); + +local comms = require("scada-common.comms") +local util = require("scada-common.util") + +local start = util.time() + +local keyd = pbkdf2() + +keyd.setPassword("mypassword") +keyd.setSalt("no_salt_thanks") +keyd.setIterations(16) +keyd.setBlockLen(4) +keyd.setDKeyLen(16) +keyd.setPRF(HMAC().setBlockSize(64).setDigest(SHA2_256)) +keyd.finish() + +util.println("pbkdf2: took " .. (util.time() - start) .. "ms") +util.println(keyd.asHex()) + +local pkt = comms.modbus_packet() +pkt.make(1, 2, 7, {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) +local spkt = comms.scada_packet() +spkt.make(1, 1, pkt.raw_sendable()) + +start = util.time() +local data = textutils.serialize(spkt.raw_sendable(), { allow_repetitions = true, compact = true }) + +util.println("packet serialize: took " .. (util.time() - start) .. "ms") +util.println("message: " .. data) + +start = util.time() +local v = { + cipher = CTRMode.Cipher, + decipher = CTRMode.Decipher, + iv = Array.fromHex("000102030405060708090A0B0C0D0E0F"), + key = Array.fromHex(keyd.asHex()), + padding = ZeroPadding +} +util.println("v init: took " .. (util.time() - start) .. "ms") + +start = util.time() +local cipher = v.cipher() +.setKey(v.key) +.setBlockCipher(AES128Cipher) +.setPadding(v.padding); +util.println("cipher init: took " .. (util.time() - start) .. "ms") + +start = util.time() +local cipherOutput = cipher + .init() + .update(Stream.fromArray(v.iv)) + .update(Stream.fromString(data)) + .asHex(); +util.println("encrypt: took " .. (util.time() - start) .. "ms") +util.println("ciphertext: " .. cipherOutput) + +start = util.time() +local decipher = v.decipher() + .setKey(v.key) + .setBlockCipher(AES128Cipher) + .setPadding(v.padding); +util.println("decipher init: took " .. (util.time() - start) .. "ms") + +start = util.time() +local plainOutput = decipher + .init() + .update(Stream.fromArray(v.iv)) + .update(Stream.fromHex(cipherOutput)) + .asHex(); +util.println("decrypt: took " .. (util.time() - start) .. "ms") +local a = Stream.fromHex(plainOutput) +local b = Stream.toString(a) +util.println("plaintext: " .. b) + +local msg = "000102030405060708090A0B0C0D0E0F" .. cipherOutput + +start = util.time() +local hash = HMAC() + .setBlockSize(64) + .setDigest(SHA1) + .setKey(keyd) + .init() + .update(Stream.fromHex(msg)) + .finish() + .asHex(); +util.println("hmac: took " .. (util.time() - start) .. "ms") +util.println("hash: " .. hash)