├── .github └── workflows │ └── test.yml ├── .luacheckrc ├── LICENSE ├── README.md ├── plc ├── aead_chacha_poly.lua ├── ascon.lua ├── base58.lua ├── base64.lua ├── base85.lua ├── bin.lua ├── blake2b.lua ├── box.lua ├── chacha20.lua ├── checksum.lua ├── ec25519.lua ├── gimli.lua ├── md5.lua ├── morus.lua ├── norx.lua ├── norx32.lua ├── poly1305.lua ├── rabbit.lua ├── rc4.lua ├── salsa20.lua ├── sha2.lua ├── sha3.lua ├── siphash.lua └── xtea.lua ├── rockspec ├── plc-0.5-1.rockspec └── plc-scm-1.rockspec ├── test ├── test_aead_chacha_poly.lua ├── test_ascon.lua ├── test_base58.lua ├── test_base64.lua ├── test_base85.lua ├── test_bin.lua ├── test_blake2b.lua ├── test_box.lua ├── test_chacha20.lua ├── test_checksum.lua ├── test_ec25519.lua ├── test_gimli.lua ├── test_md5.lua ├── test_morus.lua ├── test_norx.lua ├── test_norx32.lua ├── test_poly1305.lua ├── test_rabbit.lua ├── test_rc4.lua ├── test_salsa20.lua ├── test_sha2.lua ├── test_sha3.lua ├── test_siphash.lua └── test_xtea.lua ├── test_all.lua └── test_perf.lua /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Run plc test_all.lua 13 | run: | 14 | set -v 15 | uname -a 16 | id 17 | pwd 18 | echo "TMP=$TMP" 19 | #ls -l 20 | wget -q -O slua https://github.com/philanc/slua/raw/master/bin/slua 21 | chmod ugo+x ./slua 22 | ./slua test_all.lua 23 | -------------------------------------------------------------------------------- /.luacheckrc: -------------------------------------------------------------------------------- 1 | std = "lua53" 2 | 3 | ignore = { 4 | -- allow setting _ENV 5 | "211/_ENV", 6 | -- allow unused arguments ending with _ 7 | "212/.*_", 8 | -- allow never accessed variables ending with _ 9 | "231/.*_", 10 | -- empty if branch 11 | "542", 12 | } 13 | 14 | files["test/test_sha3.lua"] = { 15 | ignore = { 16 | -- line too long 17 | "631", 18 | } 19 | } 20 | 21 | exclude_files = { 22 | -- work in progress 23 | "plc/xtea.lua", 24 | "test/test_xtea.lua", 25 | -- functions commented out 26 | "test_perf.lua", 27 | } 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Phil Leblanc 2 | Copyright (c) 2017 Pierre Chapuis (files salsa20.lua, test_salsa20, box.lua, test_box.lua) 3 | 4 | Permission is hereby granted, free of charge, to any person 5 | obtaining a copy of this software and associated documentation 6 | files (the "Software"), to deal in the Software without restriction, 7 | including without limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of the Software, 9 | and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 19 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 20 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 21 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![CI](https://github.com/philanc/plc/workflows/CI/badge.svg) 2 | 3 | ## PLC - Pure Lua Crypto 4 | 5 | A small collection of crpytographic functions, and related utilities, 6 | implemented in pure Lua (version 5.3 or above) 7 | 8 | ### Recent changes 9 | 10 | May 2023 11 | 12 | * Added *Ascon-128*. Ascon offers authenticated encryption with associated data (AEAD), hash, XOF, MAC and PRF. Ascon has won the NIST Lightweight Cryptography competition (2023) and the CAESAR competition (2019) for lightweight authenticated encryption. 13 | 14 | December 2018 15 | 16 | * Added *XChaCha20* encryption (in plc/chacha20.lua) 17 | 18 | October 2018 19 | 20 | * Added SHA2-512 and optimized SHA2-256. Borrowed the core permutation code from the very nice pure_lua_SHA2 project by Egor Skriptunoff - https://github.com/Egor-Skriptunoff/pure_lua_SHA2 21 | 22 | April 2018 23 | 24 | * Added *Morus*, a finalist (round 4) in the CAESAR competition for authenticated encryption. 25 | 26 | * Adding *Gimli*, cryptographic functions based on the Gimli permutation (Dan Bernstein et al., 2017, https://gimli.cr.yp.to/). *Work in progress - at the moment, only the core permutation has been implemented.* 27 | 28 | * Added *Base85*, including the ZeroMQ variant of Ascii85 encoding. 29 | 30 | December 2017 31 | 32 | * Added *SipHash*, a very fast pseudorandom function (or keyed hash) 33 | optimized for speed on short messages. It can be used as a MAC and has 34 | been extensively used as a robust string hash function, as a defense 35 | against hash-flooding DoS attacks. 36 | 37 | August 2017 38 | 39 | * Added *Salsa20* and the NaCl *box() / secret_box()* API, contributed 40 | by Pierre Chapuis - https://github.com/catwell 41 | 42 | June 2017 43 | 44 | * Added *MD5*. 45 | 46 | ### Objective 47 | 48 | Collect in one place standalone implementation of well-known, and/or useful, and/or interesting cryptographic algorithms. 49 | 50 | Users should be able to pickup any file and just drop it in their project: 51 | 52 | * All the files are written in pure Lua, version 5.3 and above (tested on 5.3.4). Lua 5.3 is required since bit operators and string pack/unpack are extensively used. 53 | 54 | * The files should not require any third-party library or C extension beyond the standard Lua 5.3 library. 55 | 56 | * The files should not define any global. When required, they should just return a table with the algorithm's functions and constants. 57 | 58 | Contributions, fixes, bug reports and suggestions are welcome. 59 | 60 | What this collection is *not*: 61 | 62 | * a complete, structured cryptographic library - no promise is made about consistent API structure and documentation. This is not a library - just a collection of hopefully useful snippets of crypto source code. 63 | 64 | * high performance, heavy-duty cryptographic implementations -- after all, this is *pure* Lua... :-) 65 | 66 | * memory-efficient implementations (see above) 67 | 68 | * memory-safe algorithms -- Lua immutable strings are used and garbage-collected as needed. No guarantee is made that information, and in particular key material, is properly erased when no longer needed or do not leak. 69 | 70 | 71 | ### Functions 72 | 73 | Encryption 74 | 75 | * Morus, an *amazingly* fast (see performance below) authenticated encryption algorithm with associated data (AEAD). Morus is a finalist (round 4) in the [CAESAR](http://competitions.cr.yp.to/caesar-submissions.html) competition - see https://personal.ntu.edu.sg/wuhj/research/caesar/caesar.html 76 | 77 | * NORX, a *very* fast authenticated encryption algorithm with associated data (AEAD). NORX is a 3rd-round candidate to [CAESAR](http://competitions.cr.yp.to/caesar.html). This Lua code implements the default NORX 64-4-1 variant (state is 16 64-bit words, four rounds, no parallel execution, key and nonce are 256 bits) - see https://github.com/norx/resources 78 | 79 | * NORX32, a variant of NORX intended for smaller architectures (32-bit and less). Key and nonce are 128 bits. (Note that this NORX32 Lua implementation is half as fast as the default 64-bit NORX. It is included here only for compatibility with other implementations - In Lua, use the default NORX implementation!) 80 | 81 | * Rabbit, a fast stream cipher, selected in the eSTREAM portfolio along with Salsa20, and defined in [RFC 4503](https://tools.ietf.org/html/rfc4503) (128-bit key, 64-bit IV - see more information and links in rabbit.lua) 82 | 83 | * Chacha20, Poly1305 and authenticated stream encryption, as defined in [RFC 7539](https://tools.ietf.org/html/rfc7539), and XChacha20 stream encryption (Chacha20 with a 24-byte nonce) 84 | 85 | * Salsa20, a fast encryption algorithm and the NaCl secretbox() API for authenticated encryption (with Salsa20 and Poly1305 - see box.lua) 86 | Salsa20, Poly1305 and the NaCl library have been designed by Dan Bernstein, Tanja Lange et al. http://nacl.cr.yp.to/. 87 | 88 | * RC4 - for lightweight, low strength encryption. Can also be used as a simple pseudo-random number generator. 89 | 90 | Public key 91 | 92 | * Elliptic curve cryptography based on curve ec25519 by Dan Bernstein, Tanja Lange et al., http://nacl.cr.yp.to/. File ec25519.lua includes the core scalar multiplication operation. File box.lua includes the NaCl box() API which combines ECDH key exchange and authenticated encryption. 93 | 94 | Hash 95 | 96 | * Blake2b - Blake was a final round candidate in the NIST SHA-3 selection process. Blake2b is an improved version of Blake. See https://blake2.net/. It has been specified in [RFC 7693](https://tools.ietf.org/html/rfc7693) 97 | 98 | * SHA2 cryptographic hash family (sha256 and sha512) 99 | 100 | * SHA3 cryptographic hash family (formerly known as Keccak - 256-bit and 512-bit versions) 101 | 102 | * SipHash, a keyed hash function family optimized for speed on short messages, by Jean-Philippe Aumasson and Dan Bernstein. The variant implemented here is the default SipHash-2-4. 103 | 104 | * MD5, as specified in [RFC 1321](https://tools.ietf.org/html/rfc1321) 105 | 106 | * Non-cryptographic checksums (CRC-32, Adler-32), ... 107 | 108 | Some (un)related utilities: 109 | 110 | * Base64, Base58, Base85 (Z85, the ZeroMQ variant of Ascii85) and Hex encoding/decoding. 111 | 112 | ### In the future... 113 | 114 | Implementations that may come some day: 115 | 116 | * Ed25519 signature 117 | 118 | * better documentation in each file :-) 119 | 120 | ### Performance 121 | 122 | These crude numbers give an idea of the relative performance of the algorithms. 123 | They correspond to the encryption or the hash of a 10 MB string (10 * 1024 * 1024 bytes). 124 | 125 | They have been collected on a laptop with Linux x86_64, CPU i5 M430 @ 2.27 GHz. Lua version is 5.3.4 (ELF 64 bits) - see file 'test_perf.lua'; uncomment whatever test you want to run at the end. 126 | 127 | ``` 128 | Plain text size: 10 MBytes. Elapsed time in seconds 129 | 130 | Encryption 131 | - rc4 raw 7.4 132 | - rabbit 4.7 133 | - xtea ctr 11.0 134 | - chacha20 7.9 135 | - salsa20 8.0 136 | - norx aead 4.5 137 | - norx32 aead 9.2 138 | - morus aead 1.7 139 | 140 | Hash 141 | - md5 3.7 142 | - sha2-256 9.1 143 | - sha2-512 6.4 144 | - sha3-256 23.2 145 | - sha3-512 43.0 146 | - blake2b-512 9.4 147 | - blake2b-256 9.3 148 | - poly1305 hmac 1.2 149 | 150 | - adler-32 1.3 151 | - crc-32 1.8 152 | 153 | Elliptic curve (ec25519) 154 | - scalarmult (100 times) 18.9 155 | 156 | ``` 157 | 158 | ### Test vectors, tests, and disclaimer 159 | 160 | Some simplistic tests can be run (test_all.lua). Individual test files are provided in the 'test' directory. 161 | 162 | The implementations should pass the tests, but beyond that, there is no guarantee that these implementations conform to anything :-) -- Use at your own risk! 163 | 164 | 165 | ### License and credits 166 | 167 | All the files included here are distributed under the MIT License (see file LICENSE) 168 | 169 | The salsa20 and box/secretbox implementations are contributed by Pierre Chapuis - https://github.com/catwell 170 | 171 | The sha2-256 and sha2-512 core permutation has been borrowed from Egor Skriptunoff's pure_lua_SHA2 project - https://github.com/Egor-Skriptunoff/pure_lua_SHA2 172 | 173 | 174 | 175 | 176 | 177 | -------------------------------------------------------------------------------- /plc/aead_chacha_poly.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2015 Phil Leblanc -- see LICENSE file 2 | ------------------------------------------------------------ 3 | --[[ 4 | 5 | aead_chacha_poly 6 | 7 | Authenticated Encryption with Associated Data (AEAD) [1], based 8 | on Chacha20 stream encryption and Poly1305 MAC, as defined 9 | in RFC 7539 [2]. 10 | 11 | [1] https://en.wikipedia.org/wiki/Authenticated_encryption 12 | [2] http://www.rfc-editor.org/rfc/rfc7539.txt 13 | 14 | This file uses chacha20.lua and poly1305 for the encryption 15 | and MAC primitives. 16 | 17 | ]] 18 | 19 | local chacha20 = require "plc.chacha20" 20 | local poly1305 = require "plc.poly1305" 21 | 22 | ------------------------------------------------------------ 23 | -- poly1305 key generation 24 | 25 | local poly_keygen = function(key, nonce) 26 | local counter = 0 27 | local m = string.rep('\0', 64) 28 | local e = chacha20.encrypt(key, counter, nonce, m) 29 | -- keep only first the 256 bits (32 bytes) 30 | return e:sub(1, 32) 31 | end 32 | 33 | local pad16 = function(s) 34 | -- return null bytes to add to s so that #s is a multiple of 16 35 | return (#s % 16 == 0) and "" or ('\0'):rep(16 - (#s % 16)) 36 | end 37 | 38 | local app = table.insert 39 | 40 | local encrypt = function(aad, key, iv, constant, plain) 41 | -- aad: additional authenticated data - arbitrary length 42 | -- key: 32-byte string 43 | -- iv, constant: concatenated to form the nonce (12 bytes) 44 | -- (why not one 12-byte param? --maybe because IPsec uses 45 | -- an 8-byte nonce) 46 | -- implementation: RFC 7539 sect 2.8.1 47 | -- (memory inefficient - encr text is copied in mac_data) 48 | local mt = {} -- mac_data table 49 | local nonce = constant .. iv 50 | local otk = poly_keygen(key, nonce) 51 | local encr = chacha20.encrypt(key, 1, nonce, plain) 52 | app(mt, aad) 53 | app(mt, pad16(aad)) 54 | app(mt, encr) 55 | app(mt, pad16(encr)) 56 | -- aad and encrypted text length must be encoded as 57 | -- little endian _u64_ (and not u32) -- see errata at 58 | -- https://www.rfc-editor.org/errata_search.php?rfc=7539 59 | app(mt, string.pack(' what could be factored?) 70 | local mt = {} -- mac_data table 71 | local nonce = constant .. iv 72 | local otk = poly_keygen(key, nonce) 73 | app(mt, aad) 74 | app(mt, pad16(aad)) 75 | app(mt, encr) 76 | app(mt, pad16(encr)) 77 | app(mt, string.pack('> n) | (x << (-n & 63)) 43 | end 44 | 45 | local function ROUND(C) 46 | local t0, t1, t2, t3, t4 -- the internal t state 47 | -- addition of round constant 48 | s2 = s2 ~ C 49 | -- substitution layer 50 | s0 = s0 ~ s4 51 | s4 = s4 ~ s3 52 | s2 = s2 ~ s1 53 | -- start of keccak s-box 54 | t0 = s0 ~ ((~ s1) & s2) 55 | t1 = s1 ~ ((~ s2) & s3) 56 | t2 = s2 ~ ((~ s3) & s4) 57 | t3 = s3 ~ ((~ s4) & s0) 58 | t4 = s4 ~ ((~ s0) & s1) 59 | -- end of keccak s-box 60 | t1 = t1 ~ t0 61 | t0 = t0 ~ t4 62 | t3 = t3 ~ t2 63 | t2 = ~t2 64 | 65 | -- linear diffusion layer 66 | s0 = t0 ~ ROR(t0, 19) ~ ROR(t0, 28) 67 | s1 = t1 ~ ROR(t1, 61) ~ ROR(t1, 39) 68 | s2 = t2 ~ ROR(t2, 1) ~ ROR(t2, 6) 69 | s3 = t3 ~ ROR(t3, 10) ~ ROR(t3, 17) 70 | s4 = t4 ~ ROR(t4, 7) ~ ROR(t4, 41) 71 | end--ROUND 72 | 73 | local function P12() 74 | ROUND(0xf0) 75 | ROUND(0xe1) 76 | ROUND(0xd2) 77 | ROUND(0xc3) 78 | ROUND(0xb4) 79 | ROUND(0xa5) 80 | ROUND(0x96) 81 | ROUND(0x87) 82 | ROUND(0x78) 83 | ROUND(0x69) 84 | ROUND(0x5a) 85 | ROUND(0x4b) 86 | end--P12 87 | 88 | local function P6() 89 | ROUND(0x96) 90 | ROUND(0x87) 91 | ROUND(0x78) 92 | ROUND(0x69) 93 | ROUND(0x5a) 94 | ROUND(0x4b) 95 | end--P6 96 | 97 | local function hash(str, xoflen) 98 | -- str: the string to hash 99 | -- xoflen: the hash length. 100 | -- if not nil, function is xof 101 | -- if nil, function is regular hash. hash length is 32 102 | 103 | -- initialize state 104 | s0 = xoflen and ASCON_XOF_IV or ASCON_HASH_IV 105 | s1, s2, s3, s4 = 0, 0, 0, 0 106 | P12() 107 | 108 | -- absorb full plaintext blocks 109 | local len = #str 110 | local i = 1 -- index of current block in input string str 111 | local x 112 | while len >= ASCON_HASH_RATE do 113 | -- get 8 bytes as a big-endian uint64 114 | x, i = sunpack(">I8", str, i) 115 | s0 = s0 ~ x 116 | P12() 117 | len = len - ASCON_HASH_RATE 118 | end 119 | 120 | -- absorb final plaintext block 121 | local lastblock = str:sub(i) 122 | -- pad the last block. 123 | -- ensure there are enough zero bytes after the padding byte 124 | lastblock = lastblock .. "\x80\0\0\0\0\0\0\0\0" 125 | x = sunpack(">I8", lastblock) 126 | s0 = s0 ~ x 127 | P12() 128 | 129 | -- squeeze full output blocks 130 | local t = {} -- will contain blocks of the hash/xof digest 131 | if xoflen then len = xoflen else len = ASCON_HASH_BYTES end 132 | while len > ASCON_HASH_RATE do 133 | x = spack(">I8", s0) 134 | insert(t, x) 135 | P12() 136 | len = len - ASCON_HASH_RATE 137 | end 138 | 139 | -- squeeze final output block 140 | x = spack(">I8", s0) 141 | x = x:sub(1, len) 142 | insert(t, x) 143 | 144 | -- return the digest as a binary string 145 | x = table.concat(t, "") 146 | return x 147 | end--hash 148 | 149 | local function prf_or_mac(iv, k, str, outlen) 150 | -- internal function. Use either mac() or prf() 151 | -- which are defined after this function 152 | -- compute either mac or prf according to 153 | -- the IV used to initialize the state 154 | assert(#k == 16, "key length error. must be 16.") 155 | local K0 = sunpack(">I8", k, 1) 156 | local K1 = sunpack(">I8", k, 9) 157 | -- initialize 158 | s0 = iv 159 | s1 = K0 160 | s2 = K1 161 | s3 = 0 162 | s4 = 0 163 | P12() 164 | 165 | -- absorb full plaintext blocks 166 | local inlen = #str 167 | local ix = 1 -- index of current block in input string str 168 | local x 169 | local i 170 | 171 | -- prf absorbs 32-byte blocks, ie 4 8-byte blocks at a time 172 | while inlen >= 32 do 173 | -- get 8 bytes as a big-endian uint64 174 | x, ix = sunpack(">I8", str, ix) 175 | s0 = s0 ~ x 176 | x, ix = sunpack(">I8", str, ix) 177 | s1 = s1 ~ x 178 | x, ix = sunpack(">I8", str, ix) 179 | s2 = s2 ~ x 180 | x, ix = sunpack(">I8", str, ix) 181 | s3 = s3 ~ x 182 | P12() 183 | len = len - 32 184 | end 185 | 186 | -- absorb last block 187 | lastblock = str:sub(ix) 188 | -- pad the last block 189 | -- ensure there are enough zero bytes after the padding byte 190 | -- (the block must be at least 32-byte long 191 | lastblock = lastblock .. "\x80" .. string.rep("\0", 32) 192 | ix = 1 -- index in lastblock 193 | x, ix = sunpack(">I8", lastblock, ix) 194 | s0 = s0 ~ x 195 | x, ix = sunpack(">I8", lastblock, ix) 196 | s1 = s1 ~ x 197 | x, ix = sunpack(">I8", lastblock, ix) 198 | s2 = s2 ~ x 199 | x, ix = sunpack(">I8", lastblock, ix) 200 | s3 = s3 ~ x 201 | --domain separation 202 | s4 = s4 ~ 1 203 | P12() 204 | 205 | -- sqeeze output 206 | local len = outlen 207 | local t = {} 208 | while len > 0 do 209 | x = spack(">I8", s0) 210 | insert(t, x) 211 | x = spack(">I8", s1) 212 | insert(t, x) 213 | len = len - 16 214 | P12() 215 | end 216 | local tag = table.concat(t, "") 217 | tag = tag:sub(1, outlen) -- trim tag to the desired length 218 | return tag 219 | end--prf_or_mac 220 | 221 | local function prf(k, str, outlen) 222 | return prf_or_mac(ASCON_PRF_IV, k, str, outlen or 16) 223 | end 224 | 225 | local function mac(k, str) 226 | return prf_or_mac(ASCON_MAC_IV, k, str, 16) 227 | end 228 | 229 | local function aead_encrypt(k, n, m, ad) 230 | -- k 16-byte key 231 | -- n 16-byte nonce 232 | -- m plaintext message to encrypt 233 | -- ad associated data (default to empty string) 234 | ad = ad or "" 235 | assert(#k == 16, "key length error. must be 16.") 236 | local K0 = sunpack(">I8", k, 1) 237 | local K1 = sunpack(">I8", k, 9) 238 | local N0 = sunpack(">I8", n, 1) 239 | local N1 = sunpack(">I8", n, 9) 240 | 241 | -- initialize 242 | s0 = ASCON_128_IV 243 | s1 = K0 244 | s2 = K1 245 | s3 = N0 246 | s4 = N1 247 | P12() 248 | s3 = s3 ~ K0 249 | s4 = s4 ~ K1 250 | 251 | -- associated data 252 | local adlen = #ad 253 | if adlen > 0 then 254 | 255 | -- full associated data blocks 256 | local adlen = #ad 257 | local i = 1 -- index of current block in ad 258 | local x 259 | while adlen >= ASCON_128_RATE do 260 | -- get 8 bytes as a big-endian uint64 261 | x, i = sunpack(">I8", ad, i) 262 | s0 = s0 ~ x 263 | P6() 264 | adlen = adlen - ASCON_128_RATE 265 | end 266 | 267 | -- final associated data block 268 | local lastblock = ad:sub(i) 269 | -- pad the last block. 270 | -- ensure there are enough zero bytes after the padding byte 271 | lastblock = lastblock .. "\x80\0\0\0\0\0\0\0\0" 272 | x = sunpack(">I8", lastblock) 273 | s0 = s0 ~ x 274 | P6() 275 | end--associated data 276 | 277 | -- domain separation 278 | s4 = s4 ~ 1 279 | 280 | -- full plaintext blocks 281 | local t = {} 282 | local mlen = #m 283 | i = 1 -- index of current block in m 284 | while mlen >= ASCON_128_RATE do 285 | -- get 8 bytes as a big-endian uint64 286 | x, i = sunpack(">I8", m, i) 287 | s0 = s0 ~ x 288 | x = spack(">I8", s0) 289 | insert(t, x) 290 | P6() 291 | mlen = mlen - ASCON_128_RATE 292 | end 293 | 294 | -- final plaintext block 295 | lastblock = m:sub(i) 296 | -- pad the last block. 297 | -- ensure there are enough zero bytes after the padding byte 298 | lastblock = lastblock .. "\x80\0\0\0\0\0\0\0\0" 299 | x = sunpack(">I8", lastblock) 300 | s0 = s0 ~ x 301 | x = spack(">I8", s0) 302 | x = x:sub(1, mlen) 303 | insert(t, x) 304 | 305 | -- finalize 306 | s1 = s1 ~ K0 307 | s2 = s2 ~ K1 308 | P12() 309 | s3 = s3 ~ K0 310 | s4 = s4 ~ K1 311 | 312 | -- set tag 313 | insert(t, spack(">I8", s3)) 314 | insert(t, spack(">I8", s4)) 315 | 316 | return table.concat(t, "") 317 | end--aead_encrypt 318 | 319 | 320 | local function aead_decrypt(k, n, c, ad) 321 | ad = ad or "" 322 | assert(#k == 16, "key length error. must be 16.") 323 | local K0 = sunpack(">I8", k, 1) 324 | local K1 = sunpack(">I8", k, 9) 325 | local N0 = sunpack(">I8", n, 1) 326 | local N1 = sunpack(">I8", n, 9) 327 | s0 = ASCON_128_IV 328 | s1 = K0 329 | s2 = K1 330 | s3 = N0 331 | s4 = N1 332 | P12() 333 | s3 = s3 ~ K0 334 | s4 = s4 ~ K1 335 | 336 | -- associated data 337 | local adlen = #ad 338 | if adlen > 0 then 339 | -- full associated data blocks 340 | local adlen = #ad 341 | local i = 1 -- index of current block in ad 342 | local x 343 | while adlen >= ASCON_128_RATE do 344 | -- get 8 bytes as a big-endian uint64 345 | x, i = sunpack(">I8", ad, i) 346 | s0 = s0 ~ x 347 | P6() 348 | adlen = adlen - ASCON_128_RATE 349 | end 350 | 351 | -- final associated data block 352 | local lastblock = ad:sub(i) 353 | -- pad the last block. 354 | -- ensure there are enough zero bytes after the padding byte 355 | lastblock = lastblock .. "\x80\0\0\0\0\0\0\0\0" 356 | x = sunpack(">I8", lastblock) 357 | s0 = s0 ~ x 358 | P6() 359 | end--associated data 360 | 361 | -- domain separation 362 | s4 = s4 ~ 1 363 | 364 | -- decrypt full ciphertext blocks 365 | local clen = #c 366 | clen = clen - CRYPTO_ABYTES 367 | local mlen = clen 368 | local t = {} 369 | local i = 1 370 | local x, c0 371 | while clen >= ASCON_128_RATE do 372 | c0, i = sunpack(">I8", c, i) 373 | x = spack(">I8", c0 ~ s0) 374 | insert(t, x) 375 | s0 = c0 376 | P6() 377 | clen = clen - ASCON_128_RATE 378 | end 379 | 380 | -- decrypt final ciphertext block 381 | local lastblock = c:sub(i, i+clen-1) .. "\0\0\0\0\0\0\0\0" 382 | c0, i = sunpack(">I8", lastblock, 1) 383 | x = spack(">I8", c0 ~ s0) 384 | x = x:sub(1, clen) 385 | insert(t, x) 386 | s0 = s0 & (0xffffffffffffffff >> (clen * 8)) 387 | s0 = s0 | c0 388 | s0 = s0 ~ ( 0x80 << (56 - clen*8 )) 389 | 390 | -- finalize 391 | s1 = s1 ~ K0 392 | s2 = s2 ~ K1 393 | P12() 394 | s3 = s3 ~ K0 395 | s4 = s4 ~ K1 396 | 397 | --~ -- display decr tag: 398 | --~ print("decr tag", s2x(spack(">I8>I8", s3, s4))) 399 | --~ print("cmsg tag", s2x(c:sub(-16))) 400 | 401 | -- verify tag 402 | local T0, T1 = sunpack(">I8>I8", c, #c-15) 403 | local r = (T0 == s3 and T1 == s4) -- not constant time :-) 404 | -- tmp: check decryption w/o auth 405 | if r then 406 | return table.concat(t, "") 407 | else 408 | return nil, "decryption error" 409 | end 410 | 411 | end--aead_decrypt 412 | 413 | 414 | ------------------------------------------------------------------------ 415 | -- the ascon module 416 | 417 | return { 418 | hash = hash, 419 | mac = mac, 420 | prf = prf, 421 | aead_encrypt = aead_encrypt, 422 | aead_decrypt = aead_decrypt, 423 | } 424 | 425 | -------------------------------------------------------------------------------- /plc/base58.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2015 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------ 4 | --[[ 5 | 6 | base58 encode/decode functions 7 | 8 | Usual Base58 alphabets: (see wikipedia) 9 | Bitcoin address 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz 10 | Ripple address rpshnaf39wBUDNEGHJKLM4PQRST7VWXYZ2bcdeCg65jkm8oFqi1tuvAxyz 11 | Flick short URL 123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ 12 | 13 | The alphabet used in this module is the Bitcoin adress encoding alphabet: 14 | 123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz 15 | 16 | Note: 17 | Base58 encoding, contrary to Base64, is not intended to encode long 18 | strings. Base64 can encode long strings 3 bytes at a time. 19 | On the contrary, Base58 treats the string to be encoded as a long number 20 | encoded in Base256 (each byte is a digit) and perform an arithmetic 21 | conversion of this long number to base58. 22 | 23 | 24 | ]] 25 | 26 | 27 | local byte, char, concat = string.byte, string.char, table.concat 28 | 29 | local b58chars = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" 30 | 31 | local function encode(s) 32 | local q, b 33 | local et = {} 34 | local zn = 0 -- number of leading zero bytes in s 35 | -- assume s is a large, little-endian binary number 36 | -- with base256 digits (each byte is a "digit") 37 | local nt = {} -- number to divide in base 256, big endian 38 | local dt = {} -- result of nt // 58, in base 256 39 | local more = true -- used to count leading zero bytes 40 | for i = 1, #s do 41 | b = byte(s, i) 42 | if more and b == 0 then 43 | zn = zn + 1 44 | else 45 | more = false 46 | end 47 | nt[i] = b 48 | end 49 | if #s == zn then --take care of strings empty or with only nul bytes 50 | return string.rep('1', zn) 51 | end 52 | more = true 53 | while more do 54 | local r = 0 55 | more = false 56 | for i = 1, #nt do 57 | b = nt[i] + (256 * r) 58 | q = b // 58 59 | -- if q is not null at least once, we are good 60 | -- for another division by 58 61 | more = more or q > 0 62 | r = b % 58 63 | dt[i] = q 64 | end 65 | -- r is the next base58 digit. insert it before previous ones 66 | -- to get a big-endian base58 number 67 | table.insert(et, 1, char(byte(b58chars, r+1))) 68 | -- now copy dt into nt before another round of division by 58 69 | nt = {} 70 | for i = 1, #dt do nt[i] = dt[i] end 71 | dt = {} 72 | end--while 73 | -- don't forget the leading zeros ('1' is digit 0 in bitcoin base58 alphabet) 74 | return string.rep('1', zn) .. concat(et) 75 | end --encode() 76 | 77 | -- inverse base58 map, used by b58decode: b58charmap maps characters in 78 | -- base58 alphabet to their _offset_ in b58chars (0-based, not 1-based...) 79 | -- eg. for digit '1' b64charmap[65] == 0 and for 'z', b64charmap[122] == 57 80 | -- 81 | local b58charmap = {}; 82 | for i = 1, 58 do b58charmap[byte(b58chars, i)] = i - 1 end 83 | 84 | local function decode(s) 85 | -- reject invalid encoded strings 86 | if string.find(s, "[^"..b58chars.."]") then 87 | return nil, "invalid char" 88 | end 89 | -- process leading zeros - count and remove them 90 | local zn -- number of leading zeros (base58 digits '1') 91 | zn = #(string.match(s, "^(1+)") or "") 92 | s = string.gsub(s, "^(1+)", "") 93 | -- special case: the string is empty or contains only null bytes 94 | if s == "" then 95 | return string.rep('\x00', zn) 96 | end 97 | -- 98 | -- process significant digits 99 | local dn -- decoded number as an array of bytes (little-endian) 100 | local d -- base58 digit, as an integer 101 | local b -- a byte in dn 102 | local m -- a byte multiplied by 58 (used for product) 103 | local carry 104 | dn = { b58charmap[byte(s, 1)] } --init with most significant digit 105 | for i = 2, #s do --repeat until no more digits 106 | -- multiply dn by 58, then add next digit 107 | d = b58charmap[byte(s, i)] -- next digit 108 | carry = 0 109 | -- multiply dn by 58 110 | for j = 1, #dn do 111 | b = dn[j] 112 | m = b * 58 + carry 113 | b = m & 0xff 114 | carry = m >> 8 115 | dn[j] = b 116 | end 117 | if carry > 0 then dn[#dn + 1] = carry end 118 | -- add next digit to dn 119 | carry = d 120 | for j = 1, #dn do 121 | b = dn[j] + carry 122 | carry = b >> 8 123 | dn[j] = b & 0xff 124 | end 125 | if carry > 0 then dn[#dn + 1] = carry end 126 | end 127 | -- now dn contains the decoded number (little endian) 128 | -- must add leading zeros and reverse dn to build binary string 129 | local ben = {} -- big-endian number as array of chars 130 | local ln = #dn 131 | for i = 1, ln do 132 | ben[i] = char(dn[ln-i+1]) 133 | end 134 | return string.rep('\x00', zn) .. concat(ben) 135 | end --decode() 136 | 137 | 138 | return { -- base58 module 139 | encode = encode, 140 | decode = decode, 141 | } 142 | -------------------------------------------------------------------------------- /plc/base64.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2015 Phil Leblanc -- see LICENSE file 2 | ------------------------------------------------------------------------ 3 | 4 | -- base64 encode / decode 5 | 6 | local byte, char, concat = string.byte, string.char, table.concat 7 | 8 | local B64CHARS = -- Base64 alphabet (RFC 4648) 9 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" 10 | 11 | -- inverse base64 map, used by b64decode: b64charmap maps characters in 12 | -- base64 alphabet to their _offset_ in b64chars (0-based, not 1-based...) 13 | -- eg. for 'A' b64charmap[65] == 0 and for '/', b64charmap[47] == 63 14 | -- 15 | local b64charmap = {}; 16 | for i = 1, 64 do b64charmap[byte(B64CHARS, i)] = i - 1 end 17 | 18 | -- filename-safe alphabet (RFC 4648): 19 | -- '+/' are respectively replaced with '-_' 20 | 21 | local function encode(s, filename_safe) 22 | -- encode binary string s. returns base64 encoded string 23 | -- correct padding ('=') is appended to encoded string 24 | -- if the encoded string is longer than 72, 25 | -- a newline is added every 72 chars. 26 | -- if optional argument filename_safe is true, '+/' are replaced 27 | -- with '-_' in encoded string and padding and newlines are removed 28 | local b64chars = B64CHARS 29 | local rn = #s % 3 30 | local st = {} 31 | local c1, c2, c3 32 | local t4 = {} 33 | local lln, maxlln = 1, 72 34 | for i = 1, #s, 3 do 35 | c1 = byte(s,i) 36 | c2 = byte(s,i+1) or 0 37 | c3 = byte(s,i+2) or 0 38 | t4[1] = char(byte(b64chars, (c1 >> 2) + 1)) 39 | t4[2] = char(byte(b64chars, (((c1 << 4)|(c2 >> 4)) & 0x3f) + 1)) 40 | t4[3] = char(byte(b64chars, (((c2 << 2)|(c3 >> 6)) & 0x3f) + 1)) 41 | t4[4] = char(byte(b64chars, (c3 & 0x3f) + 1)) 42 | st[#st+1] = concat(t4) 43 | -- insert a newline every 72 chars of encoded string 44 | lln = lln + 4 45 | if lln > maxlln then st[#st+1] = "\n"; lln = 1 end 46 | end 47 | -- process remaining bytes and padding 48 | local llx = #st -- index of last st element with data 49 | if st[llx] == "\n" then llx = llx - 1 end 50 | if rn == 2 then 51 | st[llx] = string.gsub(st[llx], ".$", "=") 52 | elseif rn == 1 then 53 | st[llx] = string.gsub(st[llx], "..$", "==") 54 | end 55 | local b = concat(st) 56 | if filename_safe then 57 | -- assume that filename safe mode is not used for very long 58 | -- strings. Making 3 copies below is not considered an issue. 59 | b = string.gsub(b, "%+", "-") 60 | b = string.gsub(b, "/", "_") 61 | b = string.gsub(b, "[%s=]", "") 62 | end 63 | return b 64 | end --encode 65 | 66 | local function decode(b) 67 | -- decode base64-encoded string 68 | -- ignore all whitespaces, newlines, and padding ('=') in b 69 | local cmap = b64charmap 70 | local e1, e2, e3, e4 71 | local st = {} 72 | local t3 = {} 73 | b = string.gsub(b, "%-", "+") 74 | b = string.gsub(b, "_", "/") 75 | b = string.gsub(b, "[=%s]", "") -- remove all whitespaces and '=' 76 | if b:find("[^0-9A-Za-z/+=]") then return nil, "invalid char" end 77 | for i = 1, #b, 4 do 78 | e1 = cmap[byte(b, i)] 79 | e2 = cmap[byte(b, i+1)] 80 | if not e1 or not e2 then return nil, "invalid length" end 81 | e3 = cmap[byte(b, i+2)] 82 | e4 = cmap[byte(b, i+3)] 83 | t3[1] = char((e1 << 2) | (e2 >> 4)) 84 | if not e3 then 85 | t3[2] = nil 86 | t3[3] = nil 87 | st[#st + 1] = concat(t3) 88 | break 89 | end 90 | t3[2] = char(((e2 << 4) | (e3 >> 2)) & 0xff) 91 | if not e4 then 92 | t3[3] = nil 93 | st[#st + 1] = concat(t3) 94 | break 95 | end 96 | t3[3] = char(((e3 << 6) | (e4)) & 0xff) 97 | st[#st + 1] = concat(t3) 98 | end --for 99 | return concat(st) 100 | end --decode 101 | 102 | 103 | return { 104 | -- base64 module 105 | encode = encode, 106 | decode = decode, 107 | } 108 | -------------------------------------------------------------------------------- /plc/base85.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2018 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------------------ 4 | --[[ 5 | 6 | === z85 - the ZeroMQ variant of Ascii85 encoding 7 | 8 | Ascii85 encodes binary strings by using five displayable ascii characters 9 | to represent 4 bytes of data. 10 | 11 | The Z85 encoding alphabet is designed to facilitate embedding encoded 12 | strings in source code or scripts (eg. double-quote, single-quote, 13 | backslash are not used). 14 | 15 | Specification: https://rfc.zeromq.org/spec:32/Z85/ 16 | 17 | The Z85 specification makes no provision for padding. The application 18 | must ensure that the length of the string to encode is a multiple of 4. 19 | 20 | TODO(?): add an option to use the RFC 1924 [1] alphabet 21 | which is maybe better because "This character set excludes the 22 | characters "',./:[\] , making it suitable for use in JSON strings" [2] 23 | 24 | [1] https://tools.ietf.org/html/rfc1924 25 | [2] https://en.wikipedia.org/wiki/Ascii85 26 | 27 | 28 | Note: the original Ascii85/btoa will not be implemented. The alphabet is 29 | less convenient that the two variants above, and the special characters 30 | for groups of 4 NULs or 4 spaces are not useful for compressed 31 | and/or encrypted data. 32 | 33 | ]] 34 | 35 | local spack, sunpack = string.pack, string.unpack 36 | local byte, char = string.byte, string.char 37 | local insert, concat = table.insert, table.concat 38 | 39 | -- Z85 alphabet - see https://rfc.zeromq.org/spec:32/Z85/ 40 | local chars = 41 | "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 42 | .. ".-:+=^!/*?&<>()[]{}@%$#" 43 | 44 | local inv = {} -- maps base85 digit ascii representation to their value 45 | for i = 1, #chars do inv[byte(chars, i)] = i - 1 end 46 | 47 | local function encode(s) 48 | local n, r1, r2, r3, r4, r5 49 | -- #s must be multiple of 4 bytes 50 | assert(#s % 4 == 0, "string length must be multiple of 4 bytes") 51 | local et = {} -- used to collect encoded blocks of 4 bytes 52 | for i = 1, #s, 4 do 53 | n = sunpack(">I4", s, i) 54 | --~ print(i, n) 55 | r5 = n % 85 ; n = n // 85 56 | r4 = n % 85 ; n = n // 85 57 | r3 = n % 85 ; n = n // 85 58 | r2 = n % 85 ; n = n // 85 59 | r1 = n % 85 ; n = n // 85 60 | local eb = char( 61 | chars:byte(r1 + 1), 62 | chars:byte(r2 + 1), 63 | chars:byte(r3 + 1), 64 | chars:byte(r4 + 1), 65 | chars:byte(r5 + 1)) 66 | insert(et, eb) 67 | end--for 68 | return table.concat(et) 69 | end 70 | 71 | local function decode(e) 72 | local st = {} -- used to collect decoded blocks of 4 bytes 73 | local n, r1, r2, r3, r4, r5 74 | if #e % 5 ~= 0 then 75 | -- encoded length must be multiple of 5 bytes 76 | return nil, "invalid length" 77 | end 78 | for i = 1, #e, 5 do 79 | n = 0 80 | for j = 0, 4 do 81 | r = inv[e:byte(i+j)] 82 | if not r then 83 | return nil, "invalid char" 84 | end 85 | n = n * 85 + r 86 | end 87 | local sb = spack(">I4", n) 88 | insert(st, sb) 89 | end--for 90 | return table.concat(st) 91 | end 92 | 93 | local z85 = { 94 | encode = encode, 95 | decode = decode, 96 | } 97 | 98 | return z85 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /plc/bin.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2015 Phil Leblanc -- see LICENSE file 2 | ------------------------------------------------------------------------ 3 | --[[ 4 | 5 | bin: misc binary data utilities: 6 | 7 | stohex - encode a string as a hex string 8 | hextos - decode a hex string 9 | 10 | rotr32 - rotate right the 32 lower bits of an integer (int64) 11 | rotl32 - rotate left the 32 lower bits of an integer 12 | 13 | xor1 - xor a string with a key (repeated as needed) 14 | xor64 - same as xor1, but more efficient, memory-wise 15 | xor8 - same, but assume #key is a multiple of 8 (more efficient) 16 | 17 | 18 | ]] 19 | ------------------------------------------------------------------------ 20 | -- some local definitions 21 | 22 | local strf = string.format 23 | local byte, char = string.byte, string.char 24 | local spack, sunpack = string.pack, string.unpack 25 | 26 | local app, concat = table.insert, table.concat 27 | 28 | ------------------------------------------------------------------------ 29 | 30 | local function stohex(s, ln, sep) 31 | -- stohex(s [, ln [, sep]]) 32 | -- return the hex encoding of string s 33 | -- ln: (optional) a newline is inserted after 'ln' bytes 34 | -- ie. after 2*ln hex digits. Defaults to no newlines. 35 | -- sep: (optional) separator between bytes in the encoded string 36 | -- defaults to nothing (if ln is nil, sep is ignored) 37 | -- example: 38 | -- stohex('abcdef', 4, ":") => '61:62:63:64\n65:66' 39 | -- stohex('abcdef') => '616263646566' 40 | -- 41 | if #s == 0 then return "" end 42 | if not ln then -- no newline, no separator: do it the fast way! 43 | return (s:gsub('.', 44 | function(c) return strf('%02x', byte(c)) end 45 | )) 46 | end 47 | sep = sep or "" -- optional separator between each byte 48 | local t = {} 49 | for i = 1, #s - 1 do 50 | t[#t + 1] = strf("%02x%s", s:byte(i), 51 | (i % ln == 0) and '\n' or sep) 52 | end 53 | -- last byte, without any sep appended 54 | t[#t + 1] = strf("%02x", s:byte(#s)) 55 | return concat(t) 56 | end --stohex() 57 | 58 | local function hextos(hs, unsafe) 59 | -- decode an hex encoded string. return the decoded string 60 | -- if optional parameter unsafe is defined, assume the hex 61 | -- string is well formed (no checks, no whitespace removal). 62 | -- Default is to remove white spaces (incl newlines) 63 | -- and check that the hex string is well formed 64 | local tonumber = tonumber 65 | if not unsafe then 66 | hs = string.gsub(hs, "%s+", "") -- remove whitespaces 67 | if string.find(hs, '[^0-9A-Za-z]') or #hs % 2 ~= 0 then 68 | error("invalid hex string") 69 | end 70 | end 71 | return hs:gsub( '(%x%x)', 72 | function(c) return char(tonumber(c, 16)) end 73 | ) 74 | end -- hextos 75 | 76 | local function rotr32(i, n) 77 | -- rotate right on 32 bits 78 | return ((i >> n) | (i << (32 - n))) & 0xffffffff 79 | end 80 | 81 | local function rotl32(i, n) 82 | -- rotate left on 32 bits 83 | return ((i << n) | (i >> (32 - n))) & 0xffffffff 84 | end 85 | 86 | 87 | 88 | 89 | local function xor1(key, plain) 90 | -- return a string which is a xor of plain and key 91 | -- plain and key may have arbitrary length. 92 | -- the result has the same length as plain. 93 | -- naive implementation, one byte at a time (ok for small strings) 94 | local ot = {} 95 | local ki, kln = 1, #key 96 | for i = 1, #plain do 97 | ot[#ot + 1] = char(byte(plain, i) ~ byte(key, ki)) 98 | ki = ki + 1 99 | if ki > kln then ki = 1 end 100 | end 101 | return concat(ot) 102 | end --xor1 103 | 104 | local function xor8(key, plain) 105 | -- return a string which is a xor of plain and key 106 | -- plain may have arbitrary length. 107 | -- ** key length (in bytes) must be a multiple of 8 ** 108 | -- the result has the same length as plain. 109 | -- (result is computed one uint64 at a time) 110 | assert(#key % 8 == 0, 'key not a multiple of 8 bytes') 111 | local ka = {} -- key as an array of uint64 112 | for i = 1, #key, 8 do 113 | -- !!beware below: () around sunpack are needed: 114 | -- app aka table.insert takes optionally 3 args 115 | -- and sunpack returns 2 args... 116 | app(ka, (sunpack("> n) | (x << (64-n)) 29 | end 30 | 31 | -- G Mixing function. 32 | 33 | local function G(v, a, b, c, d, x, y) 34 | v[a] = v[a] + v[b] + x 35 | v[d] = ROTR64(v[d] ~ v[a], 32) 36 | v[c] = v[c] + v[d] 37 | v[b] = ROTR64(v[b] ~ v[c], 24) 38 | v[a] = v[a] + v[b] + y 39 | v[d] = ROTR64(v[d] ~ v[a], 16) 40 | v[c] = v[c] + v[d] 41 | v[b] = ROTR64(v[b] ~ v[c], 63) 42 | end 43 | 44 | -- Initialization Vector. 45 | local iv = { 46 | 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 47 | 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 48 | 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 49 | 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179 50 | } 51 | 52 | local sigma = { 53 | -- array index start at 1 in Lua, 54 | -- => all the permutation values are incremented by one 55 | { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, 56 | { 15, 11, 5, 9, 10, 16, 14, 7, 2, 13, 1, 3, 12, 8, 6, 4 }, 57 | { 12, 9, 13, 1, 6, 3, 16, 14, 11, 15, 4, 7, 8, 2, 10, 5 }, 58 | { 8, 10, 4, 2, 14, 13, 12, 15, 3, 7, 6, 11, 5, 1, 16, 9 }, 59 | { 10, 1, 6, 8, 3, 5, 11, 16, 15, 2, 12, 13, 7, 9, 4, 14 }, 60 | { 3, 13, 7, 11, 1, 12, 9, 4, 5, 14, 8, 6, 16, 15, 2, 10 }, 61 | { 13, 6, 2, 16, 15, 14, 5, 11, 1, 8, 7, 4, 10, 3, 9, 12 }, 62 | { 14, 12, 8, 15, 13, 2, 4, 10, 6, 1, 16, 5, 9, 7, 3, 11 }, 63 | { 7, 16, 15, 10, 12, 4, 1, 9, 13, 3, 14, 8, 2, 5, 11, 6 }, 64 | { 11, 3, 9, 5, 8, 7, 2, 6, 16, 12, 10, 15, 4, 13, 14, 1 }, 65 | { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, 66 | { 15, 11, 5, 9, 10, 16, 14, 7, 2, 13, 1, 3, 12, 8, 6, 4 } 67 | } 68 | 69 | 70 | local function compress(ctx, last) 71 | -- Compression function. "last" flag indicates last block. 72 | local v, m = {}, {} -- both v and m are u64[16] 73 | for i = 1, 8 do 74 | v[i] = ctx.h[i] 75 | v[i+8] = iv[i] 76 | end 77 | v[13] = v[13] ~ ctx.t[1] -- low 64 bits of offset 78 | v[14] = v[14] ~ ctx.t[2] -- high 64 bits 79 | if last then v[15] = ~v[15] end 80 | for i = 0, 15 do -- get little-endian words 81 | m[i+1] = sunpack(" 64 or (key and #key > 64) then 113 | return nil, "illegal parameters" 114 | end 115 | local ctx = {h={}, t={}, c=1, outlen=outlen} -- the blake2 context 116 | -- note: ctx.c is the index of 1st byte free in input buffer (ctx.b) 117 | -- it is not used in this implementation 118 | for i = 1, 8 do ctx.h[i] = iv[i] end -- state, "param block" 119 | ctx.h[1] = ctx.h[1] ~ 0x01010000 ~ (keylen << 8) ~ outlen 120 | ctx.t[1] = 0 --input count low word 121 | ctx.t[2] = 0 --input count high word 122 | -- zero input block 123 | ctx.b = "" 124 | if keylen > 0 then 125 | update(ctx, key) 126 | -- ctx.c = 128 -- pad b with zero bytes 127 | ctx.b = ctx.b .. string.rep('\0', 128 - #ctx.b) 128 | assert(#ctx.b == 128) 129 | end 130 | return ctx 131 | end --init() 132 | 133 | update = function(ctx, data) 134 | -- buffer mgt cannot be done the C way.. 135 | local bln, rln, iln 136 | local i = 1 -- index of 1st byte to process in data 137 | while true do 138 | bln = #ctx.b -- current number of bytes in the input buffer 139 | assert(bln <= 128) 140 | if bln == 128 then --ctx.b is full; process it. 141 | -- add counters 142 | ctx.t[1] = ctx.t[1] + 128 143 | -- warning: this is a signed 64bit addition 144 | -- here it is assumed that the total input is less 145 | -- than 2^63 bytes (this should be enough for a 146 | -- pure Lua implementation!) => ctx.t[1] overflow is ignored. 147 | compress(ctx, false) -- false means not last 148 | ctx.b = "" -- empty buffer 149 | else -- ctx.b is not full; append more bytes from data 150 | rln = 128 - bln -- remaining space (in bytes) in ctx.b 151 | iln = #data - i + 1 -- number of bytes yet to process in data 152 | if iln < rln then 153 | ctx.b = ctx.b .. data:sub(i, i + iln -1) 154 | -- here, all data bytes have been processed or put in 155 | -- buffer and buffer is not full. we are done. 156 | break 157 | else 158 | ctx.b = ctx.b .. data:sub(i, i + rln -1) 159 | i = i + rln 160 | end 161 | end 162 | end--while 163 | end --update() 164 | 165 | local function final(ctx) 166 | -- finalize the hash and return the digest as a string 167 | -- 168 | local bln = #ctx.b 169 | -- add number of remaining bytes in buffer (ignore carry overflow) 170 | ctx.t[1] = ctx.t[1] + bln 171 | -- pad the buffer with zero bytes 172 | local rln = 128 - bln -- remaining space (in bytes) in ctx.b 173 | ctx.b = ctx.b .. string.rep('\0', rln) 174 | compress(ctx, true) -- true means final block 175 | -- extract the digest (outlen bytes long) 176 | local outtbl = {} 177 | for i = 0, ctx.outlen - 1 do 178 | outtbl[i+1] = string.char( 179 | (ctx.h[(i >> 3) + 1] >> (8 * (i & 7))) & 0xff) 180 | end 181 | local dig = concat(outtbl) 182 | return dig 183 | end --final() 184 | 185 | local function hash(data, outlen, key) 186 | -- convenience function 187 | -- return the hash of data as a string 188 | -- outlen is optional digest length (1..64) - defaults to 64 189 | -- key is an optional key string (length must be 1..64) 190 | local ctx, msg = init(outlen, key) 191 | if not ctx then return ctx, msg end 192 | update(ctx, data) 193 | return final(ctx) 194 | end --hash 195 | 196 | 197 | ------------------------------------------------------------------------ 198 | 199 | 200 | return { --black2b module 201 | init = init, 202 | update = update, 203 | final = final, 204 | hash = hash, 205 | } 206 | 207 | 208 | 209 | 210 | -------------------------------------------------------------------------------- /plc/box.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2017 Pierre Chapuis -- see LICENSE file 2 | ------------------------------------------------------------ 3 | --[[ 4 | 5 | High-level encryption routines 6 | 7 | ]] 8 | 9 | local salsa20 = require "plc.salsa20" 10 | local ec25519 = require "plc.ec25519" 11 | local poly1305 = require "plc.poly1305" 12 | 13 | local function public_key(sk) 14 | assert(type(sk) == "string", "sk must be a string") 15 | assert(#sk == 32, "#sh must be 32") 16 | sk = table.pack(sk:byte(1, 32)) 17 | local pk = {} 18 | ec25519.crypto_scalarmult_base(pk, sk) 19 | return string.char(table.unpack(pk)) 20 | end 21 | 22 | local function unpack_nonce(nonce) 23 | assert(#nonce == 24, "#nonce must be 24") 24 | local nonce1 = nonce:sub(1, 8) 25 | local counter = string.unpack("= 16, "#et must be at least 16") 44 | local counter, nonce1, nonce2 = unpack_nonce(nonce) 45 | local key2 = salsa20.hsalsa20(key, counter, nonce1) 46 | local key3 = salsa20.stream(key2, 0, nonce2, 32) 47 | local mac = et:sub(1, 16) 48 | local mac2 = poly1305.auth(et:sub(17), key3) 49 | if mac2 ~= mac then return nil, "invalid MAC" end 50 | local pt = salsa20.encrypt(key2, 0, nonce2, string.rep("\0", 16) .. et) 51 | return pt:sub(33) 52 | end 53 | 54 | local function stream_key(pk, sk) 55 | assert(#pk == 32, "#pk must be 32") 56 | assert(#sk == 32, "#pk must be 32") 57 | pk = table.pack(pk:byte(1, 32)) 58 | sk = table.pack(sk:byte(1, 32)) 59 | local k = {} 60 | ec25519.crypto_scalarmult(k, sk, pk) 61 | k = string.char(table.unpack(k)) 62 | return salsa20.hsalsa20(k, 0, string.rep("\0", 8)) 63 | end 64 | 65 | local function box(pt, nonce, pk_b, sk_a) 66 | return secretbox(pt, nonce, stream_key(pk_b, sk_a)) 67 | end 68 | 69 | local function box_open(et, nonce, pk_a, sk_b) 70 | return secretbox_open(et, nonce, stream_key(pk_a, sk_b)) 71 | end 72 | 73 | return { 74 | public_key = public_key, 75 | secretbox = secretbox, 76 | secretbox_open = secretbox_open, 77 | stream_key = stream_key, 78 | box = box, 79 | box_open = box_open, 80 | } 81 | -------------------------------------------------------------------------------- /plc/chacha20.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2018 Phil Leblanc -- see LICENSE file 2 | ------------------------------------------------------------ 3 | --[[ 4 | 5 | Chacha20 stream encryption 6 | 7 | Pure Lua implementation of the chacha20 algorithm 8 | 9 | This implements the IETF variant of chacha20 encryption 10 | as defined in RFC 7539 (12-byte nonce) and the xchacha20 11 | variant (same encryption algorithm, but with a 24-byte nonce) 12 | 13 | For the combined authenticated encryption with associated 14 | data (AEAD) based on chacha20 encryption and poly1305 15 | authentication, see the aead_chacha20.lua file 16 | 17 | 18 | See also: 19 | - many chacha20 links at 20 | http://ianix.com/pub/chacha-deployment.html 21 | 22 | ]] 23 | 24 | local app, concat = table.insert, table.concat 25 | 26 | ------------------------------------------------------------ 27 | 28 | -- chacha quarter round (rotl inlined) 29 | local function qround(st,x,y,z,w) 30 | -- st is a chacha state: an array of 16 u32 words 31 | -- x,y,z,w are indices in st 32 | local a, b, c, d = st[x], st[y], st[z], st[w] 33 | local t 34 | a = (a + b) & 0xffffffff 35 | --d = rotl32(d ~ a, 16) 36 | t = d ~ a ; d = ((t << 16) | (t >> (16))) & 0xffffffff 37 | c = (c + d) & 0xffffffff 38 | --b = rotl32(b ~ c, 12) 39 | t = b ~ c ; b = ((t << 12) | (t >> (20))) & 0xffffffff 40 | a = (a + b) & 0xffffffff 41 | --d = rotl32(d ~ a, 8) 42 | t = d ~ a ; d = ((t << 8) | (t >> (24))) & 0xffffffff 43 | c = (c + d) & 0xffffffff 44 | --b = rotl32(b ~ c, 7) 45 | t = b ~ c ; b = ((t << 7) | (t >> (25))) & 0xffffffff 46 | st[x], st[y], st[z], st[w] = a, b, c, d 47 | return st 48 | end 49 | 50 | -- chacha20 state and working state are allocated once and reused 51 | -- by each invocation of chacha20_block() 52 | local chacha20_state = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 53 | local chacha20_working_state = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 54 | 55 | local chacha20_block = function(key, counter, nonce) 56 | -- key: u32[8] 57 | -- counter: u32 58 | -- nonce: u32[3] 59 | local st = chacha20_state -- state 60 | local wst = chacha20_working_state -- working state 61 | -- initialize state 62 | st[1], st[2], st[3], st[4] = 63 | 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 64 | for i = 1, 8 do st[i+4] = key[i] end 65 | st[13] = counter 66 | for i = 1, 3 do st[i+13] = nonce[i] end 67 | -- copy state to working_state 68 | for i = 1, 16 do wst[i] = st[i] end 69 | -- run 20 rounds, ie. 10 iterations of 8 quarter rounds 70 | for _ = 1, 10 do --RFC reference: 71 | qround(wst, 1,5,9,13) --1. QUARTERROUND ( 0, 4, 8,12) 72 | qround(wst, 2,6,10,14) --2. QUARTERROUND ( 1, 5, 9,13) 73 | qround(wst, 3,7,11,15) --3. QUARTERROUND ( 2, 6,10,14) 74 | qround(wst, 4,8,12,16) --4. QUARTERROUND ( 3, 7,11,15) 75 | qround(wst, 1,6,11,16) --5. QUARTERROUND ( 0, 5,10,15) 76 | qround(wst, 2,7,12,13) --6. QUARTERROUND ( 1, 6,11,12) 77 | qround(wst, 3,8,9,14) --7. QUARTERROUND ( 2, 7, 8,13) 78 | qround(wst, 4,5,10,15) --8. QUARTERROUND ( 3, 4, 9,14) 79 | end 80 | -- add working_state to state 81 | for i = 1, 16 do st[i] = (st[i] + wst[i]) & 0xffffffff end 82 | -- return st, an array of 16 u32 words used as a keystream 83 | return st 84 | end --chacha20_block() 85 | 86 | -- pat16: used to unpack a 64-byte string as 16 uint32 87 | local pat16 = "= 64) 107 | local ba = table.pack(string.unpack(pat16, pt, ptidx)) 108 | local keystream = chacha20_block(key, counter, nonce) 109 | for i = 1, 16 do 110 | ba[i] = ba[i] ~ keystream[i] 111 | end 112 | local es = string.pack(pat16, table.unpack(ba)) 113 | if rbn < 64 then 114 | es = string.sub(es, 1, rbn) 115 | end 116 | return es 117 | end --chacha20_encrypt_block 118 | 119 | local chacha20_encrypt = function(key, counter, nonce, pt) 120 | -- encrypt plain text 'pt', return encrypted text 121 | -- key: 32 bytes as a string 122 | -- counter: an uint32 (must be incremented for each block) 123 | -- nonce: 8 bytes as a string 124 | -- pt: plain text string, 125 | 126 | -- ensure counter can fit an uint32 --although it's unlikely 127 | -- that we hit this wall with pure Lua encryption :-) 128 | assert((counter + #pt // 64 + 1) < 0xffffffff, 129 | "block counter must fit an uint32") 130 | assert(#key == 32, "#key must be 32") 131 | assert(#nonce == 12, "#nonce must be 12") 132 | local keya = table.pack(string.unpack(" (1 << 40) then error("adler32: string too large") end 22 | for i = 1,#s do 23 | local b = byte(s, i) 24 | s1 = s1 + b 25 | s2 = s2 + s1 26 | -- no need to test or compute mod prime every turn. 27 | end 28 | s1 = s1 % prime 29 | s2 = s2 % prime 30 | return (s2 << 16) + s1 31 | end --adler32() 32 | 33 | local function crc32_nt(s) 34 | -- return crc32 checksum of string s as an integer 35 | -- uses no lookup table 36 | -- inspired by crc32b at 37 | -- http://www.hackersdelight.org/hdcodetxt/crc.c.txt 38 | local b, crc, mask 39 | crc = 0xffffffff 40 | for i = 1, #s do 41 | b = byte(s, i) 42 | crc = crc ~ b 43 | for _ = 1, 8 do --eight times 44 | mask = -(crc & 1) 45 | crc = (crc >> 1) ~ (0xedb88320 & mask) 46 | end 47 | end--for 48 | return (~crc) & 0xffffffff 49 | end --crc32_nt() 50 | 51 | local function crc32(s, lt) 52 | -- return crc32 checksum of string as an integer 53 | -- use lookup table lt if provided or create one on the fly 54 | -- if lt is empty, it is initialized. 55 | lt = lt or {} 56 | local b, crc, mask 57 | if not lt[1] then -- setup table 58 | for i = 1, 256 do 59 | crc = i - 1 60 | for _ = 1, 8 do --eight times 61 | mask = -(crc & 1) 62 | crc = (crc >> 1) ~ (0xedb88320 & mask) 63 | end 64 | lt[i] = crc 65 | end--for 66 | end--if 67 | -- compute the crc 68 | crc = 0xffffffff 69 | for i = 1, #s do 70 | b = byte(s, i) 71 | crc = (crc >> 8) ~ lt[((crc ~ b) & 0xFF) + 1] 72 | end 73 | return (~crc) & 0xffffffff 74 | end --crc32() 75 | 76 | 77 | return { 78 | -- module 79 | adler32 = adler32, 80 | crc32_nt = crc32_nt, 81 | crc32 = crc32, 82 | } 83 | -------------------------------------------------------------------------------- /plc/ec25519.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2015 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------ 4 | --[[ 5 | 6 | ec25519 - curve25519 scalar multiplication 7 | 8 | Ported to Lua from the original C tweetnacl implementation, 9 | (public domain, by Dan Bernstein, Tanja Lange et al 10 | see http://tweetnacl.cr.yp.to/ ) 11 | 12 | To make debug and validation easier, the original code structure 13 | and function names have been conserved as much as possible. 14 | 15 | 16 | 17 | ]] 18 | 19 | ------------------------------------------------------------ 20 | 21 | -- set25519() not used 22 | 23 | local function car25519(o) 24 | local c 25 | for i = 1, 16 do 26 | o[i] = o[i] + 65536 -- 1 << 16 27 | -- lua ">>" doesn't perform sign extension... 28 | -- so the following >>16 doesn't work with negative numbers!! 29 | -- ...took a bit of time to find this one :-) 30 | -- c = o[i] >> 16 31 | c = o[i] // 65536 32 | if i < 16 then 33 | o[i+1] = o[i+1] + (c - 1) 34 | else 35 | o[1] = o[1] + 38 * (c - 1) 36 | end 37 | o[i] = o[i] - (c << 16) 38 | end 39 | end --car25519() 40 | 41 | local function sel25519(p, q, b) 42 | local c = ~(b-1) 43 | local t 44 | for i = 1, 16 do 45 | t = c & (p[i] ~ q[i]) 46 | p[i] = p[i] ~ t 47 | q[i] = q[i] ~ t 48 | end 49 | end --sel25519 50 | 51 | local function pack25519(o, n) 52 | -- out o[32], in n[16] 53 | local m, t = {}, {} 54 | local b 55 | for i = 1, 16 do t[i] = n[i] end 56 | car25519(t) 57 | car25519(t) 58 | car25519(t) 59 | for _ = 1, 2 do 60 | m[1] = t[1] - 0xffed 61 | for i = 2, 15 do 62 | m[i] = t[i] - 0xffff - ((m[i-1] >> 16) & 1) 63 | m[i-1] = m[i-1] & 0xffff 64 | end 65 | m[16] = t[16] - 0x7fff - ((m[15] >> 16) & 1) 66 | b = (m[16] >> 16) & 1 67 | m[15] = m[15] & 0xffff 68 | sel25519(t, m, 1-b) 69 | end 70 | for i = 1, 16 do 71 | o[2*i-1] = t[i] & 0xff 72 | o[2*i] = t[i] >> 8 73 | end 74 | end -- pack25519 75 | 76 | -- neq25519() not used 77 | -- par25519() not used 78 | 79 | local function unpack25519(o, n) 80 | -- out o[16], in n[32] 81 | for i = 1, 16 do 82 | o[i] = n[2*i-1] + (n[2*i] << 8) 83 | end 84 | o[16] = o[16] & 0x7fff 85 | end -- unpack25519 86 | 87 | local function A(o, a, b) --add 88 | for i = 1, 16 do o[i] = a[i] + b[i] end 89 | end 90 | 91 | local function Z(o, a, b) --sub 92 | for i = 1, 16 do o[i] = a[i] - b[i] end 93 | end 94 | 95 | local function M(o, a, b) --mul gf, gf -> gf 96 | local t = {} 97 | for i = 1, 32 do t[i] = 0 end 98 | for i = 1, 16 do 99 | for j = 1, 16 do 100 | t[i+j-1] = t[i+j-1] + (a[i] * b[j]) 101 | end 102 | end 103 | for i = 1, 15 do t[i] = t[i] + 38 * t[i+16] end 104 | for i = 1, 16 do o[i] = t[i] end 105 | car25519(o) 106 | car25519(o) 107 | end 108 | 109 | local function S(o, a) --square 110 | M(o, a, a) 111 | end 112 | 113 | local function inv25519(o, i) 114 | local c = {} 115 | for a = 1, 16 do c[a] = i[a] end 116 | for a = 253, 0, -1 do 117 | S(c, c) 118 | if a ~= 2 and a ~= 4 then M(c, c, i) end 119 | end 120 | for a = 1, 16 do o[a] = c[a] end 121 | --~ pt(o) 122 | end 123 | 124 | --pow2523() not used 125 | 126 | local t_121665 = {0xDB41,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 127 | 128 | local function crypto_scalarmult(q, n, p) 129 | -- out q[], in n[], in p[] 130 | local z = {} 131 | local x = {} 132 | local a = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 133 | local b = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 134 | local c = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 135 | local d = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 136 | local e = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 137 | local f = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 138 | for i = 1, 31 do z[i] = n[i] end 139 | z[32] = (n[32] & 127) | 64 140 | z[1] = z[1] & 248 141 | --~ pt(z) 142 | unpack25519(x, p) 143 | --~ pt(x) 144 | for i = 1, 16 do 145 | b[i] = x[i] 146 | a[i] = 0 147 | c[i] = 0 148 | d[i] = 0 149 | end 150 | a[1] = 1 151 | d[1] = 1 152 | for i = 254, 0, -1 do 153 | local r = (z[(i>>3)+1] >> (i & 7)) & 1 154 | sel25519(a,b,r) 155 | sel25519(c,d,r) 156 | A(e,a,c) 157 | Z(a,a,c) 158 | A(c,b,d) 159 | Z(b,b,d) 160 | S(d,e) 161 | S(f,a) 162 | M(a,c,a) 163 | M(c,b,e) 164 | A(e,a,c) 165 | Z(a,a,c) 166 | S(b,a) 167 | Z(c,d,f) 168 | M(a,c,t_121665) 169 | A(a,a,d) 170 | M(c,c,a) 171 | M(a,d,f) 172 | M(d,b,x) 173 | S(b,e) 174 | sel25519(a,b,r) 175 | sel25519(c,d,r) 176 | end 177 | for i = 1, 16 do 178 | x[i+16] = a[i] 179 | x[i+32] = c[i] 180 | x[i+48] = b[i] 181 | x[i+64] = d[i] 182 | end 183 | -- cannot use pointer arithmetics... 184 | local x16, x32 = {}, {} 185 | for i = 1, #x do 186 | if i > 16 then x16[i-16] = x[i] end 187 | if i > 32 then x32[i-32] = x[i] end 188 | end 189 | inv25519(x32,x32) 190 | M(x16,x16,x32) 191 | pack25519(q,x16) 192 | return 0 193 | end -- crypto_scalarmult 194 | 195 | local t_9 = { -- u8 * 32 196 | 9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 197 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 198 | } 199 | 200 | local function crypto_scalarmult_base(q, n) 201 | -- out q[], in n[] 202 | return crypto_scalarmult(q, n, t_9) 203 | end 204 | 205 | ------------------------------------------------------------------------ 206 | -- convenience function (using binary strings instead of byte tables) 207 | -- 208 | -- curve point and scalars are represented as 32-byte binary strings 209 | -- (encoded as little endian) 210 | 211 | local function scalarmult(n, p) 212 | -- n, a scalar (little endian) as a 32-byte string 213 | -- p, a curve point as a 32-byte string 214 | -- return the scalar product np as a 32-byte string 215 | local qt, nt, pt = {}, {}, {} 216 | for i = 1, 32 do 217 | nt[i] = string.byte(n, i) 218 | pt[i] = string.byte(p, i) 219 | end 220 | crypto_scalarmult(qt, nt, pt) 221 | local q = string.char(table.unpack(qt)) 222 | return q 223 | end 224 | 225 | -- base: the curve point generator = 9 226 | 227 | local base = '\9' .. ('\0'):rep(31) 228 | 229 | --[[ 230 | 231 | --- How to use scalarmult to generate curve25519 keypairs 232 | 233 | For any scalar s (a 32-byte string), the scalar product 's . base' is a point on the elliptic curve. 'base' is a generator. 234 | point = scalarmult(s, base) 235 | 236 | If s2 is another scalar, 237 | point2 = scalarmult(s2, point) is also a point on the curve. 238 | 239 | Secret keys are random scalars (a 32-byte random string). 240 | The associated public keys are the corresponding points on the curve 241 | (also encoded as 32-byte strings): 242 | public = scalarmult(secret, base) 243 | 244 | let 'ask' and 'bsk' be repectively Alice and Bob private keys 245 | (random 32-byte strings) 246 | 247 | Alice public key 'apk' is obtained by: 248 | apk = scalarmult(ask, base) 249 | 250 | Similarly for Bob public key 'bpk': 251 | bpk = scalarmult(bsk, base) 252 | 253 | --- How to perform an ECDH exchange 254 | 255 | When Alice wants to send a message to Bob, they must be able to establish 256 | a common session key (for a symmetric encryption algorithm). 257 | 258 | Alice and Bob each have the public key of the other party. 259 | 260 | Alice compute a secret s: 261 | s = scalarmult(ask, bpk) 262 | 263 | Bob is able to compute the same secret s as: 264 | s = scalarmult(bsk, apk) 265 | 266 | This is the same secret since apk = ask . base and bpk = bsk . base 267 | thus: s = ask . bsk . base = bsk . ask . base since the scalar 268 | multiplication is commutative 269 | 270 | the secret s is a 32-byte string. It is advised not to use it 271 | directly as a session key since the bit dstribution in s is not 272 | completely uniform. So the secret s should be "washed" with 273 | a cryptographic hash function to get a uniformly distributed key. 274 | 275 | NaCl use the Salsa20 core encryption function to do that (see 276 | the hsalsa20() function and the stream_key() function in file 277 | box.lua 278 | 279 | But any other hash function can do. (eg. sha256 or blake2b) 280 | 281 | --- scalarmult implementation notes 282 | 283 | - scalars are represented as little endian 284 | 285 | - the 3 lower bits of the scalar are ignored (the actual scalar 286 | is contained in bits 3 to 255) 287 | so for example, 288 | scalarmult(('\0'):rep(32), base) 289 | == scalarmult('\6' .. ('\0'):rep(31), base) 290 | 291 | - the bit 254 of the scalar is always set. so: 292 | scalarmult(('\0'):rep(32), base) 293 | == scalarmult(('\0'):rep(31) .. '\x40', base) 294 | 295 | - the group order N is (2^252 + 27742317777372353535851937790883648493) 296 | let N8 the 32-byte string containing 8 * N as a little endian 297 | 32-byte int. (the hex rep of the 32-byte string is: 298 | 689faee7d21893c0b2e6bc17f5cef7a600000000000000000000000000000080 ) 299 | then, 300 | scalarmult(('\0'):rep(32), base) == scalarmult(N8, base) 301 | 302 | 303 | 304 | ]] 305 | 306 | 307 | return { 308 | crypto_scalarmult = crypto_scalarmult, 309 | crypto_scalarmult_base = crypto_scalarmult_base, 310 | -- 311 | -- convenience function and definition 312 | -- 313 | scalarmult = scalarmult, 314 | base = base, 315 | -- 316 | } 317 | -- end of ec25519 module 318 | 319 | 320 | 321 | -------------------------------------------------------------------------------- /plc/gimli.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2018 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------------------ 4 | --[[ 5 | 6 | !!! WORK IN PROGRESS !!! 7 | !!! Only the permutation is exposed at the moment !!! 8 | 9 | The Gimli permutation by Dan Bernstein et al. (2017) 10 | - see Gimli links and authors list at https://gimli.cr.yp.to/ 11 | 12 | Note: The performance of this Lua implementation has been disappointing: 13 | 14 | To encrypt a 10 Mbyte string, with a 16-byte block, the number of 15 | permutations is approximately 10 * 1024 * 1024 / 16. 16 | 17 | On an average laptop - CPU i5 M430 @ 2.27 GHz, Linux 4.4 x86_64, 18 | Lua 5.3.4, the elapsed time for these permutations is: 19 | -- gimli_core32: 42-43s -- direct implementation 20 | -- gimli_core32opt: 20-21s -- Lua-optimized version 21 | 22 | The times for complete encryption would be higher. Which makes a 23 | pure Lua gimli-based encryption not competitive with established 24 | algorithms such as Salsa20 or Chacha20 (7-8s on the same laptop) 25 | 26 | Given the performance, the encryption and hash functions have 27 | not been implemented. 28 | 29 | 30 | ]] 31 | 32 | ------------------------------------------------------------------------ 33 | -- local definitions 34 | 35 | local spack, sunpack = string.pack, string.unpack 36 | local byte, char = string.byte, string.char 37 | local insert, concat = table.insert, table.concat 38 | 39 | -- display the permutation state (an array of 12 32-byte integers) 40 | --~ local function px12(st) 41 | --~ local fmt = "%08x %08x %08x %08x \n" 42 | --~ .. "%08x %08x %08x %08x \n" 43 | --~ .. "%08x %08x %08x %08x " 44 | --~ print(string.format(fmt, 45 | --~ st[1], st[2], st[3], st[4], 46 | --~ st[5], st[6], st[7], st[8], 47 | --~ st[9], st[10], st[11], st[12])) 48 | --~ end 49 | 50 | ------------------------------------------------------------------------ 51 | -- the gimli core permutation 52 | 53 | local function rotate(x, n) 54 | return ((x << n) | (x >> (32-n))) & 0xffffffff 55 | end 56 | 57 | local function gimli_core32(st) 58 | -- this is the default implementation, based on the C reference code 59 | local x, y, z 60 | for round = 24, 1, -1 do 61 | for col = 1, 4 do 62 | x = rotate(st[col], 24) 63 | y = rotate(st[col+4], 9) 64 | z = st[col+8] 65 | st[col+8] = (x ~ (z << 1) ~ ((y & z) << 2)) & 0xffffffff 66 | st[col+4] = (y ~ x ~ ((x | z) << 1)) & 0xffffffff 67 | st[col] = (z ~ y ~ ((x & y) << 3)) & 0xffffffff 68 | end--for 69 | -- !! 1-indexed arrays !! st[i] in C is st[i+1] in Lua 70 | if (round & 3) == 0 then 71 | -- // small swap: pattern s...s...s... etc. 72 | x = st[1] 73 | st[1] = st[2] 74 | st[2] = x 75 | x = st[3] 76 | st[3] = st[4] 77 | st[4] = x 78 | end 79 | if (round & 3) == 2 then 80 | -- // big swap: pattern ..S...S...S. etc. 81 | x = st[1] 82 | st[1] = st[3] 83 | st[3] = x 84 | x = st[2] 85 | st[2] = st[4] 86 | st[4] = x 87 | end 88 | if (round & 3) == 0 then 89 | -- // add constant: pattern c...c...c... etc. 90 | st[1] = st[1] ~ (0x9e377900 | round) 91 | end 92 | --~ px12(st) 93 | --~ print("^^ ----------------------", round) 94 | end--for rounds 95 | 96 | end--gimli_core32() 97 | 98 | 99 | 100 | local function gimli_core32opt(st) 101 | -- core permutation optimized for Lua 102 | -- state copied to local variables for the state, 103 | -- the "for col..." loop is unrolled 104 | -- rotates are inlined 105 | -- the unneeded 32-clamp after rotate ("& 0xffffffff") is removed 106 | 107 | local x, y, z 108 | local st1, st2, st3, st4, st5, st6, st7, st8, st9, st10, st11, st12 = 109 | st[1],st[2],st[3],st[4],st[5],st[6], 110 | st[7],st[8],st[9],st[10],st[11],st[12] 111 | 112 | for round = 24, 1, -1 do 113 | --unroll "for col..." 114 | --~ for col = 1, 4 do 115 | --~ x = rotate(st[col], 24) 116 | --~ y = rotate(st[col+4], 9) 117 | --~ z = st[col+8] 118 | --~ st[col+8] = (x ~ (z << 1) ~ ((y & z) << 2)) & 0xffffffff 119 | --~ st[col+4] = (y ~ x ~ ((x | z) << 1)) & 0xffffffff 120 | --~ st[col] = (z ~ y ~ ((x & y) << 3)) & 0xffffffff 121 | --col=1 122 | --~ x = rotate(st1, 24) 123 | --~ y = rotate(st5, 9) 124 | x = ((st1 << 24) | (st1 >> (8))) --& 0xffffffff 125 | y = ((st5 << 9) | (st5 >> (23))) --& 0xffffffff 126 | z = st9 127 | st9 = (x ~ (z << 1) ~ ((y & z) << 2)) & 0xffffffff 128 | st5 = (y ~ x ~ ((x | z) << 1)) & 0xffffffff 129 | st1 = (z ~ y ~ ((x & y) << 3)) & 0xffffffff 130 | --col=2 131 | --~ x = rotate(st2, 24) 132 | --~ y = rotate(st6, 9) 133 | x = ((st2 << 24) | (st2 >> (8))) --& 0xffffffff 134 | y = ((st6 << 9) | (st6 >> (23))) --& 0xffffffff 135 | z = st10 136 | st10 = (x ~ (z << 1) ~ ((y & z) << 2)) & 0xffffffff 137 | st6 = (y ~ x ~ ((x | z) << 1)) & 0xffffffff 138 | st2 = (z ~ y ~ ((x & y) << 3)) & 0xffffffff 139 | --col=3 140 | --~ x = rotate(st3, 24) 141 | --~ y = rotate(st7, 9) 142 | x = ((st3 << 24) | (st3 >> (8))) --& 0xffffffff 143 | y = ((st7 << 9) | (st7 >> (23))) --& 0xffffffff 144 | z = st11 145 | st11 = (x ~ (z << 1) ~ ((y & z) << 2)) & 0xffffffff 146 | st7 = (y ~ x ~ ((x | z) << 1)) & 0xffffffff 147 | st3 = (z ~ y ~ ((x & y) << 3)) & 0xffffffff 148 | --col=4 149 | --~ x = rotate(st4, 24) 150 | --~ y = rotate(st8, 9) 151 | x = ((st4 << 24) | (st4 >> (8))) --& 0xffffffff 152 | y = ((st8 << 9) | (st8 >> (23))) --& 0xffffffff 153 | z = st12 154 | st12 = (x ~ (z << 1) ~ ((y & z) << 2)) & 0xffffffff 155 | st8 = (y ~ x ~ ((x | z) << 1)) & 0xffffffff 156 | st4 = (z ~ y ~ ((x & y) << 3)) & 0xffffffff 157 | --~ end--for unrolled 158 | -- !! 1-indexed arrays !! st[i] in C is st[i+1] in Lua 159 | if (round & 3) == 0 then 160 | -- // small swap: pattern s...s...s... etc. 161 | x = st1 162 | st1 = st2 163 | st2 = x 164 | x = st3 165 | st3 = st4 166 | st4 = x 167 | end 168 | if (round & 3) == 2 then 169 | -- // big swap: pattern ..S...S...S. etc. 170 | x = st1 171 | st1 = st3 172 | st3 = x 173 | x = st2 174 | st2 = st4 175 | st4 = x 176 | end 177 | if (round & 3) == 0 then 178 | -- // add constant: pattern c...c...c... etc. 179 | st1 = st1 ~ (0x9e377900 | round) 180 | end 181 | --~ px12(st) 182 | --~ print("^^ ----------------------", round) 183 | end--for rounds 184 | st[1],st[2],st[3],st[4],st[5],st[6],st[7],st[8],st[9],st[10],st[11],st[12] 185 | = st1, st2, st3, st4, st5, st6, st7, st8, st9, st10, st11, st12 186 | 187 | end--gimli_core32opt() 188 | 189 | ------------------------------------------------------------------------ 190 | -- the gimli module 191 | return { 192 | -- the core permutation is exposed to facilitate tests and allow 193 | -- arbitrary constructions by module user. 194 | gimli_core32 = gimli_core32, 195 | gimli_core32opt = gimli_core32opt, 196 | -- 197 | --~ gimli_encrypt = gimli_encrypt, 198 | --~ gimli_decrypt = gimli_decrypt, 199 | --~ gimli_hash = gimli_hash, 200 | -- 201 | } 202 | -------------------------------------------------------------------------------- /plc/md5.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2017 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------------------ 4 | -- md5 hash - see RFC 1321 - https://www.ietf.org/rfc/rfc1321.txt 5 | 6 | local spack, sunpack = string.pack, string.unpack 7 | 8 | ------------------------------------------------------------------------ 9 | 10 | local function FF(a, b, c, d, x, s, ac) 11 | a = (a + ((b & c) | ((~b) & d)) + x + ac) & 0xffffffff 12 | a = ((a << s) | (a >> (32-s))) & 0xffffffff 13 | a = (a + b) & 0xffffffff 14 | return a 15 | end 16 | 17 | local function GG(a, b, c, d, x, s, ac) 18 | a = (a + ((b & d) | c & (~d) ) + x + ac) & 0xffffffff 19 | a = ((a << s) | (a >> (32-s))) & 0xffffffff 20 | a = (a + b) & 0xffffffff 21 | return a 22 | end 23 | 24 | local function HH(a, b, c, d, x, s, ac) 25 | a = (a + ((b ~ c ~ d)) + x + ac) & 0xffffffff 26 | a = ((a << s) | (a >> (32-s))) & 0xffffffff 27 | a = (a + b) & 0xffffffff 28 | return a 29 | end 30 | 31 | local function II(a, b, c, d, x, s, ac) 32 | a = (a + (c ~ (b | ~d)) + x + ac) & 0xffffffff 33 | a = ((a << s) | (a >> (32-s))) & 0xffffffff 34 | a = (a + b) & 0xffffffff 35 | return a 36 | end 37 | 38 | local function transform(state, input, i, t) 39 | -- process the 64-byte input block in string 'input' at offset 'i' 40 | -- t is a uint32[16] array. It is passed as a parameter 41 | -- for performance reasons 42 | -- 43 | local a, b, c, d = state[1], state[2], state[3], state[4] 44 | 45 | -- load array 46 | for j = 1, 16 do 47 | t[j] = sunpack("= 64 do 139 | -- process block 140 | transform(state, input, i, ibt) 141 | i = i + 64 -- update input index 142 | r = r - 64 -- update number of unprocessed bytes 143 | end 144 | -- finalize. must append to input a mandatory 0x80 byte, some 145 | -- padding, and the input bit-length ('inputbits') 146 | local lastblock -- the rest of input .. some padding .. inputbits 147 | local padlen -- padding length in bytes 148 | if r < 56 then padlen = 55 - r else padlen = 119 - r end 149 | lastblock = input:sub(i) -- remaining input 150 | .. '\x80' .. ('\0'):rep(padlen) --padding 151 | .. spack(" mac 21 | -- compute the mac for a message m and a key k 22 | -- this is tha main API of the module 23 | 24 | the following functions should be used only if the MAC must be 25 | computed over several message parts. 26 | 27 | init(k) -> state 28 | -- initialize the poly1305 state with a key 29 | update(state, m) -> state 30 | -- update the state with a fragment of a message 31 | finish(state) -> mac 32 | -- finalize the computation and return the MAC 33 | 34 | Note: several update() can be called between init() and finish(). 35 | For every invocation but the last, the fragment of message m 36 | passed to update() must have a length multiple of 16 bytes: 37 | st = init(k) 38 | update(st, m1) -- here, #m1 % 16 == 0 39 | update(st, m2) -- here, #m2 % 16 == 0 40 | update(st, m3) -- here, #m3 can be arbitrary 41 | mac = finish(st) 42 | 43 | The simple API auth(m, k) is implemented as 44 | st = init(k) 45 | update(st, m) -- #m can be arbitrary 46 | mac = finish(st) 47 | 48 | Credits: 49 | This poly1305 Lua implementation is based on the cool 50 | poly1305-donna C 32-bit implementation (just try to figure 51 | out the h * r mod (2^130-5) computation!) by Andrew Moon, 52 | https://github.com/floodyberry/poly1305-donna 53 | 54 | See also: 55 | - many chacha20 links at 56 | http://ianix.com/pub/chacha-deployment.html 57 | 58 | ]] 59 | 60 | ----------------------------------------------------------- 61 | -- poly1305 62 | 63 | local sunp = string.unpack 64 | 65 | local function poly_init(k) 66 | -- k: 32-byte key as a string 67 | -- initialize internal state 68 | local st = { 69 | r = { 70 | (sunp('> 2) & 0x3ffff03, --r1 72 | (sunp('> 4) & 0x3ffc0ff, --r2 73 | (sunp('> 6) & 0x3f03fff, --r3 74 | (sunp('> 8) & 0x00fffff, --r4 75 | }, 76 | h = { 0,0,0,0,0 }, 77 | pad = { sunp('= 16 do -- 16 = poly1305_block_size 112 | -- h += m[i] (in rfc: a += n with 0x01 byte) 113 | h0 = h0 + ((sunp('> 2) & 0x3ffffff) 115 | h2 = h2 + ((sunp('> 4) & 0x3ffffff) 116 | h3 = h3 + ((sunp('> 6) & 0x3ffffff) 117 | h4 = h4 + ((sunp('> 8) | hibit)--0x01 byte 118 | -- 119 | -- h *= r % p (partial) 120 | d0 = h0*r0 + h1*s4 + h2*s3 + h3*s2 + h4*s1 121 | d1 = h0*r1 + h1*r0 + h2*s4 + h3*s3 + h4*s2 122 | d2 = h0*r2 + h1*r1 + h2*r0 + h3*s4 + h4*s3 123 | d3 = h0*r3 + h1*r2 + h2*r1 + h3*r0 + h4*s4 124 | d4 = h0*r4 + h1*r3 + h2*r2 + h3*r1 + h4*r0 125 | -- 126 | c = (d0>>26) & 0xffffffff ; h0 = d0 & 0x3ffffff 127 | d1 = d1 + c ; c = (d1>>26) & 0xffffffff ; h1 = d1 & 0x3ffffff 128 | d2 = d2 + c ; c = (d2>>26) & 0xffffffff ; h2 = d2 & 0x3ffffff 129 | d3 = d3 + c ; c = (d3>>26) & 0xffffffff ; h3 = d3 & 0x3ffffff 130 | d4 = d4 + c ; c = (d4>>26) & 0xffffffff ; h4 = d4 & 0x3ffffff 131 | h0 = h0 + (c*5) ; c = h0>>26 ; h0 = h0 & 0x3ffffff 132 | h1 = h1 + c 133 | -- 134 | midx = midx + 16 -- 16 = poly1305_block_size 135 | bytes = bytes - 16 136 | end --while 137 | st.h[1] = h0 138 | st.h[2] = h1 139 | st.h[3] = h2 140 | st.h[4] = h3 141 | st.h[5] = h4 142 | st.bytes = bytes -- remaining bytes. must be < 16 here 143 | st.midx = midx -- index of first remaining bytes 144 | return st 145 | end --poly_blocks() 146 | 147 | local function poly_update(st, m) 148 | -- st: internal state 149 | -- m: message:string 150 | st.bytes, st.midx = #m, 1 151 | -- process full blocks if any 152 | if st.bytes >= 16 then 153 | poly_blocks(st, m) 154 | end 155 | --handle remaining bytes 156 | if st.bytes == 0 then -- no bytes left 157 | -- nothing to do? no add 0x01? - apparently not. 158 | else 159 | local buffer = string.sub(m, st.midx) 160 | .. '\x01' .. string.rep('\0', 16 - st.bytes -1) 161 | assert(#buffer == 16) 162 | st.final = true -- this is the last block 163 | --~ p16(buffer) 164 | poly_blocks(st, buffer) 165 | end 166 | -- 167 | return st 168 | end --poly_update 169 | 170 | local function poly_finish(st) 171 | -- 172 | local c, mask --u32 173 | local f --u64 174 | -- fully carry h 175 | local h0 = st.h[1] 176 | local h1 = st.h[2] 177 | local h2 = st.h[3] 178 | local h3 = st.h[4] 179 | local h4 = st.h[5] 180 | -- 181 | c = h1 >> 26; h1 = h1 & 0x3ffffff 182 | h2 = h2 + c; c = h2 >> 26; h2 = h2 & 0x3ffffff 183 | h3 = h3 + c; c = h3 >> 26; h3 = h3 & 0x3ffffff 184 | h4 = h4 + c; c = h4 >> 26; h4 = h4 & 0x3ffffff 185 | h0 = h0 + (c*5); c = h0 >> 26; h0 = h0 & 0x3ffffff 186 | h1 = h1 + c 187 | -- 188 | --compute h + -p 189 | local g0 = (h0 + 5) ; c = g0 >> 26; g0 = g0 & 0x3ffffff 190 | local g1 = (h1 + c) ; c = g1 >> 26; g1 = g1 & 0x3ffffff 191 | local g2 = (h2 + c) ; c = g2 >> 26; g2 = g2 & 0x3ffffff 192 | local g3 = (h3 + c) ; c = g3 >> 26; g3 = g3 & 0x3ffffff 193 | local g4 = (h4 + c - 0x4000000) &0xffffffff -- (1 << 26) 194 | -- 195 | -- select h if h < p, or h + -p if h >= p 196 | mask = ((g4 >> 31) -1) & 0xffffffff 197 | -- 198 | g0 = g0 & mask 199 | g1 = g1 & mask 200 | g2 = g2 & mask 201 | g3 = g3 & mask 202 | g4 = g4 & mask 203 | -- 204 | mask = (~mask) & 0xffffffff 205 | h0 = (h0 & mask) | g0 206 | h1 = (h1 & mask) | g1 207 | h2 = (h2 & mask) | g2 208 | h3 = (h3 & mask) | g3 209 | h4 = (h4 & mask) | g4 210 | -- 211 | --h = h % (2^128) 212 | h0 = ((h0 ) | (h1 << 26)) & 0xffffffff 213 | h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff 214 | h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff 215 | h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff 216 | -- 217 | -- mac = (h + pad) % (2^128) 218 | f = h0 + st.pad[1] ; h0 = f & 0xffffffff 219 | f = h1 + st.pad[2] + (f >> 32) ; h1 = f & 0xffffffff 220 | f = h2 + st.pad[3] + (f >> 32) ; h2 = f & 0xffffffff 221 | f = h3 + st.pad[4] + (f >> 32) ; h3 = f & 0xffffffff 222 | -- 223 | local mac = string.pack('> (32 - n))) & 0xffffffff 46 | end 47 | 48 | ------------------------------------------------------------------------ 49 | 50 | local function gfunc(x) 51 | -- square a 32-bit unsigned integer 52 | -- return the upper 32 bits xor the lower 32 bits of the result 53 | local h = x * x 54 | -- looks like the mult works for arbitrary uint32 x... 55 | -- normal or happy impl detail? 56 | local l = h & 0xffffffff 57 | h = h >> 32 --logical shift: no sign extension => clean uint32 58 | return h ~ l 59 | end --gfunc 60 | 61 | local function newstate() 62 | -- return a new, empty state 63 | return { 64 | x = {0,0,0,0,0,0,0,0}, -- 8 * u32 65 | c = {0,0,0,0,0,0,0,0}, -- 8 * u32 66 | co = {0,0,0,0,0,0,0,0}, -- 8 * u32 67 | g = {0,0,0,0,0,0,0,0}, -- 8 * u32 68 | -- (co is "c_old" in reference impl. 69 | -- co and g are kept in state to 70 | -- prevent reallocation at every invocation) 71 | carry = 0 -- u32 72 | } 73 | end --newstate 74 | 75 | local function clonestate(st) 76 | -- return a copy of state st 77 | -- (allows to keep a "master" state for a same key setup 78 | -- and create "working" states for different IV) 79 | local nst = newstate() 80 | for i = 1, 8 do 81 | nst.x[i] = st.x[i] 82 | nst.c[i] = st.c[i] 83 | end 84 | nst.carry = st.carry 85 | return nst 86 | end --clonestate 87 | 88 | local function nextstate(st) 89 | local x, c, co, g = st.x, st.c, st.co, st.g 90 | local rl = rotl32 91 | for i = 1, 8 do co[i] = c[i] end 92 | -- compute new counter values 93 | c[1] = (c[1] + 0x4D34D34D + st.carry) & 0xffffffff 94 | c[2] = (c[2] + 0xD34D34D3 + (c[1] < co[1] and 1 or 0)) & 0xffffffff 95 | c[3] = (c[3] + 0x34D34D34 + (c[2] < co[2] and 1 or 0)) & 0xffffffff 96 | c[4] = (c[4] + 0x4D34D34D + (c[3] < co[3] and 1 or 0)) & 0xffffffff 97 | c[5] = (c[5] + 0xD34D34D3 + (c[4] < co[4] and 1 or 0)) & 0xffffffff 98 | c[6] = (c[6] + 0x34D34D34 + (c[5] < co[5] and 1 or 0)) & 0xffffffff 99 | c[7] = (c[7] + 0x4D34D34D + (c[6] < co[6] and 1 or 0)) & 0xffffffff 100 | c[8] = (c[8] + 0xD34D34D3 + (c[7] < co[7] and 1 or 0)) & 0xffffffff 101 | st.carry = c[8] < co[8] and 1 or 0 102 | -- compute the g-values 103 | for i = 1, 8 do g[i] = gfunc((x[i] + c[i]) & 0xffffffff) end 104 | -- compute new state values (don't forget arrays are 1-based!!) 105 | x[1] = (g[1] + rl(g[8],16) + rl(g[7],16)) & 0xffffffff 106 | x[2] = (g[2] + rl(g[1],8) + g[8]) & 0xffffffff 107 | x[3] = (g[3] + rl(g[2],16) + rl(g[1],16)) & 0xffffffff 108 | x[4] = (g[4] + rl(g[3],8) + g[2]) & 0xffffffff 109 | x[5] = (g[5] + rl(g[4],16) + rl(g[3],16)) & 0xffffffff 110 | x[6] = (g[6] + rl(g[5],8) + g[4]) & 0xffffffff 111 | x[7] = (g[7] + rl(g[6],16) + rl(g[5],16)) & 0xffffffff 112 | x[8] = (g[8] + rl(g[7],8) + g[6]) & 0xffffffff 113 | -- done 114 | end --nextstate 115 | 116 | local function keysetup(st, key) 117 | -- key is a 16 byte string 118 | assert(#key == 16) 119 | local k1, k2, k3, k4 = sunpack(">16)) 127 | x[4] = ((k1<<16) & 0xffffffff | (k4>>16)) 128 | x[6] = ((k2<<16) & 0xffffffff | (k1>>16)) 129 | x[8] = ((k3<<16) & 0xffffffff | (k2>>16)) 130 | -- initial counter values 131 | c[1] = rotl32(k3, 16) 132 | c[3] = rotl32(k4, 16) 133 | c[5] = rotl32(k1, 16) 134 | c[7] = rotl32(k2, 16) 135 | c[2] = (k1 & 0xffff0000) | (k2 & 0xffff) 136 | c[4] = (k2 & 0xffff0000) | (k3 & 0xffff) 137 | c[6] = (k3 & 0xffff0000) | (k4 & 0xffff) 138 | c[8] = (k4 & 0xffff0000) | (k1 & 0xffff) 139 | -- 140 | st.carry = 0 141 | -- iterate 4 times 142 | for _ = 1, 4 do 143 | nextstate(st) 144 | end 145 | -- modify the counters 146 | for i = 1, 4 do c[i] = c[i] ~ x[i+4] end 147 | for i = 5, 8 do c[i] = c[i] ~ x[i-4] end 148 | end 149 | 150 | local function ivsetup(st, iv) 151 | -- iv is an 8-byte string 152 | -- 153 | assert(#iv == 8) 154 | local i1, i2, i3, i4 155 | i1, i3 = sunpack("> 16) | (i3 & 0xffff0000) 157 | i4 = (i3 << 16) & 0xffffffff | (i1 & 0x0000ffff) 158 | -- modify counter values 159 | local c = st.c 160 | c[1] = c[1] ~ i1 161 | c[2] = c[2] ~ i2 162 | c[3] = c[3] ~ i3 163 | c[4] = c[4] ~ i4 164 | c[5] = c[5] ~ i1 165 | c[6] = c[6] ~ i2 166 | c[7] = c[7] ~ i3 167 | c[8] = c[8] ~ i4 168 | -- iterate 4 times 169 | for _ = 1, 4 do nextstate(st) end 170 | -- done 171 | end --ivsetup 172 | 173 | local function processblock(st, itxt, idx) 174 | -- itxt is the input string, idx is an index in itxt 175 | -- processblock encrypts/decrypts one block, ie. 16 bytes in itxt 176 | -- processblock returns the result as a string and a flag set to true 177 | -- if this was the last block in itxt (ie. #itxt - idx <= 16) 178 | -- 179 | local i1, i2, i3, i4 -- input, as 4 uint32 words 180 | local o1, o2, o3, o4 -- output, as 4 uint32 words 181 | -- bn: byte number (used for a last, incomplete block) 182 | local bn = #itxt - idx + 1 183 | local last = bn <= 16 -- last block in itxt 184 | local short = bn < 16 -- last block, shorter than 16 bytes 185 | local fmt = "> 16) ~ ((x[4] << 16) & 0xffffffff) 195 | o2 = i2 ~ x[3] ~ (x[8] >> 16) ~ ((x[6] << 16) & 0xffffffff) 196 | o3 = i3 ~ x[5] ~ (x[2] >> 16) ~ ((x[8] << 16) & 0xffffffff) 197 | o4 = i4 ~ x[7] ~ (x[4] >> 16) ~ ((x[2] << 16) & 0xffffffff) 198 | local outstr = spack(fmt, o1, o2, o3, o4) 199 | if short then 200 | outstr = string.sub(outstr, 1, bn) 201 | end 202 | return outstr, last 203 | end --processblock 204 | 205 | local function crypt(key, iv, text) 206 | -- encrypt/decrypt a text (this is the main API of the module) 207 | -- key is a 16-byte string 208 | -- iv is a 8-byte string 209 | -- text is the text to encrypt/decrypt 210 | -- returns the encrypted/decrypted text 211 | local st = newstate() 212 | keysetup(st, key) 213 | ivsetup(st, iv) 214 | if #text == 0 then return "" end 215 | local ot = {} -- a table to collect output 216 | local ob, last -- output block, last block flag 217 | local idx = 1 218 | repeat 219 | ob, last = processblock(st, text, idx) 220 | idx = idx + 16 --next block 221 | app(ot, ob) 222 | until last 223 | return concat(ot) 224 | end --crypt 225 | 226 | 227 | ------------------------------------------------------------------------ 228 | return { -- rabbit module 229 | encrypt = crypt, 230 | decrypt = crypt, 231 | -- 232 | key_size = 16, 233 | iv_size = 8, 234 | -- 235 | -- functions for more complex scenarios 236 | newstate = newstate, 237 | clonestate = clonestate, 238 | keysetup = keysetup, 239 | ivsetup = ivsetup, 240 | processblock = processblock, 241 | } 242 | 243 | -------------------------------------------------------------------------------- /plc/rc4.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2015 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------ 4 | -- rc4 encryption / decryption 5 | 6 | local byte, char, concat = string.byte, string.char, table.concat 7 | 8 | --[[ 9 | 10 | (RC4 pseudo code, from wikipedia) 11 | 12 | key scheduling -- keylength in [1..256] bytes, typically 16 (128 bits) 13 | for i from 0 to 255 14 | S[i] := i 15 | endfor 16 | j := 0 17 | for i from 0 to 255 18 | j := (j + S[i] + key[i mod keylength]) mod 256 19 | swap values of S[i] and S[j] 20 | endfor 21 | 22 | Pseudo-random generation 23 | i := 0 24 | j := 0 25 | while GeneratingOutput: 26 | i := (i + 1) mod 256 27 | j := (j + S[i]) mod 256 28 | swap values of S[i] and S[j] 29 | K := S[(S[i] + S[j]) mod 256] 30 | output K 31 | endwhile 32 | 33 | ]] 34 | 35 | local function keysched(key) 36 | -- key must be a 16-byte string 37 | assert(#key == 16) 38 | local s = {} 39 | local j,ii,jj 40 | for i = 0, 255 do s[i+1] = i end 41 | j = 0 42 | for i = 0, 255 do 43 | ii = i+1 44 | j = (j + s[ii] + byte(key, (i % 16) + 1)) & 0xff 45 | jj = j+1 46 | s[ii], s[jj] = s[jj], s[ii] 47 | end 48 | return s 49 | end 50 | 51 | local function step(s, i, j) 52 | i = (i + 1) & 0xff 53 | local ii = i + 1 54 | j = (j + s[ii]) & 0xff 55 | local jj = j + 1 56 | s[ii], s[jj] = s[jj], s[ii] 57 | local k = s[ ((s[ii] + s[jj]) & 0xff) + 1 ] 58 | return s, i, j, k 59 | end 60 | 61 | local function rc4raw(key, plain) 62 | -- raw encryption 63 | -- key must be a 16-byte string 64 | local s = keysched(key) 65 | local i, j = 0, 0 66 | local k 67 | local t = {} 68 | for n = 1, #plain do 69 | s, i, j, k = step(s, i, j) 70 | t[n] = char(byte(plain, n) ~ k) 71 | end 72 | return concat(t) 73 | end 74 | 75 | local function rc4(key, plain, drop) 76 | -- encrypt 'plain', return encrypted text 77 | -- key must be a 16-byte string 78 | -- optional drop (default = 256): ignore first 'drop' iterations 79 | drop = drop or 256 80 | local s = keysched(key) 81 | local i, j = 0, 0 82 | local k 83 | local t = {} 84 | -- run and ignore 'drop' iterations 85 | for _ = 1, drop do 86 | s, i, j = step(s, i, j) 87 | end 88 | -- now start to encrypt 89 | for n = 1, #plain do 90 | s, i, j, k = step(s, i, j) 91 | t[n] = char(byte(plain, n) ~ k) 92 | end 93 | return concat(t) 94 | end 95 | 96 | return { -- module 97 | rc4raw = rc4raw, 98 | rc4 = rc4, 99 | encrypt = rc4, 100 | decrypt = rc4, 101 | } 102 | -------------------------------------------------------------------------------- /plc/salsa20.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2017 Pierre Chapuis -- see LICENSE file 2 | ------------------------------------------------------------ 3 | --[[ 4 | 5 | Salsa20 stream encryption 6 | 7 | Pure Lua implementation of the salsa20 algorithm 8 | 9 | ]] 10 | 11 | local app, concat = table.insert, table.concat 12 | 13 | ------------------------------------------------------------ 14 | 15 | -- salsa quarter round (rotl inlined) 16 | local function qround(st,x,y,z,w) 17 | -- st is a salsa state: an array of 16 u32 words 18 | -- x,y,z,w are indices in st 19 | local a, b, c, d = st[x], st[y], st[z], st[w] 20 | local t 21 | t = (a + d) & 0xffffffff 22 | -- b = b ~ rotl32(t, 7) 23 | b = b ~ ((t << 7) | (t >> 25)) & 0xffffffff 24 | t = (b + a) & 0xffffffff 25 | -- c = c ~ rotl32(t, 9) 26 | c = c ~ ((t << 9) | (t >> 23)) & 0xffffffff 27 | t = (c + b) & 0xffffffff 28 | -- d = d ~ rotl32(t, 13) 29 | d = d ~ ((t << 13) | (t >> 19)) & 0xffffffff 30 | t = (d + c) & 0xffffffff 31 | -- a = a ~ rotl32(t, 18) 32 | a = a ~ ((t << 18) | (t >> 14)) & 0xffffffff 33 | st[x], st[y], st[z], st[w] = a, b, c, d 34 | return st 35 | end 36 | 37 | -- salsa20 state and working state are allocated once and reused 38 | -- by each invocation of salsa20_block() 39 | local salsa20_state = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 40 | local salsa20_working_state = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} 41 | 42 | local salsa20_block = function(key, counter, nonce) 43 | -- key: u32[8] 44 | -- counter: u32[2] 45 | -- nonce: u32[2] 46 | local st = salsa20_state -- state 47 | local wst = salsa20_working_state -- working state 48 | -- initialize state 49 | st[1], st[6], st[11], st[16] = 50 | 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 51 | for i = 1, 4 do 52 | st[i+1] = key[i] 53 | st[i+11] = key[i+4] 54 | end 55 | st[7], st[8], st[9], st[10] = nonce[1], nonce[2], counter[1], counter[2] 56 | -- copy state to working_state 57 | for i = 1, 16 do wst[i] = st[i] end 58 | -- run 20 rounds, ie. 10 iterations of 8 quarter rounds 59 | for _ = 1, 10 do --RFC reference: 60 | qround(wst, 1,5,9,13) --1. QUARTERROUND ( 0, 4, 8,12) 61 | qround(wst, 6,10,14,2) --2. QUARTERROUND ( 5, 9,13, 1) 62 | qround(wst, 11,15,3,7) --3. QUARTERROUND (10,14, 2, 6) 63 | qround(wst, 16,4,8,12) --4. QUARTERROUND (15, 3, 7,11) 64 | qround(wst, 1,2,3,4) --5. QUARTERROUND ( 0, 1, 2, 3) 65 | qround(wst, 6,7,8,5) --6. QUARTERROUND ( 5, 6, 7, 4) 66 | qround(wst, 11,12,9,10) --7. QUARTERROUND (10,11, 8, 9) 67 | qround(wst, 16,13,14,15) --8. QUARTERROUND (15,12,13,14) 68 | end 69 | -- add working_state to state 70 | for i = 1, 16 do st[i] = (st[i] + wst[i]) & 0xffffffff end 71 | -- return st, an array of 16 u32 words used as a keystream 72 | return st 73 | end --salsa20_block() 74 | 75 | local function hsalsa20_block(key, counter, nonce) 76 | local st = salsa20_block(key, counter, nonce) 77 | return { 78 | (st[1] - 0x61707865) & 0xffffffff, 79 | (st[6] - 0x3320646e) & 0xffffffff, 80 | (st[11] - 0x79622d32) & 0xffffffff, 81 | (st[16] - 0x6b206574) & 0xffffffff, 82 | (st[7] - nonce[1]) & 0xffffffff, 83 | (st[8] - nonce[2]) & 0xffffffff, 84 | (st[9] - counter[1]) & 0xffffffff, 85 | (st[10] - counter[2]) & 0xffffffff, 86 | } 87 | end 88 | 89 | -- pat16: used to unpack a 64-byte string as 16 uint32 and vice versa 90 | local pat16 = "= 64) 113 | local ba = table.pack(string.unpack(pat16, pt, ptidx)) 114 | local keystream = salsa20_block(key, counter, nonce) 115 | for i = 1, 16 do 116 | ba[i] = ba[i] ~ keystream[i] 117 | end 118 | local es = string.pack(pat16, table.unpack(ba)) 119 | if rbn < 64 then 120 | es = string.sub(es, 1, rbn) 121 | end 122 | return es 123 | end --salsa20_encrypt_block 124 | 125 | local salsa20_encrypt = function(key, counter, nonce, pt) 126 | -- encrypt plain text 'pt', return encrypted text 127 | -- key: 32 bytes as a string 128 | -- counter: an uint64 (must be incremented for each block) 129 | -- nonce: 8 bytes as a string 130 | -- pt: plain text string 131 | assert(#key == 32, "#key must be 32") 132 | assert(#nonce == 8, "#nonce must be 8") 133 | local keya = table.pack(string.unpack("> 32} 136 | local t = {} -- used to collect all encrypted blocks 137 | local ptidx = 1 138 | while ptidx <= #pt do 139 | app(t, salsa20_encrypt_block(keya, countera, noncea, pt, ptidx)) 140 | ptidx = ptidx + 64 141 | countera[1] = countera[1] + 1 142 | if countera[1] > 0xffffffff then 143 | countera[1] = 0 144 | countera[2] = countera[2] + 1 145 | end 146 | end 147 | return (concat(t)) 148 | end --salsa20_encrypt() 149 | 150 | local function salsa20_stream(key, counter, nonce, length) 151 | assert(#key == 32, "#key must be 32") 152 | assert(#nonce == 8, "#nonce must be 8") 153 | local keya = table.pack(string.unpack("> 32} 156 | local t = {} -- used to collect all encrypted blocks 157 | while length > 0 do 158 | local keystream = salsa20_block(keya, countera, noncea) 159 | local block = string.pack(pat16, table.unpack(keystream)) 160 | if length <= 64 then block = block:sub(1, length) end 161 | app(t, block) 162 | length = length - 64 163 | countera[1] = countera[1] + 1 164 | if countera[1] > 0xffffffff then 165 | countera[1] = 0 166 | countera[2] = countera[2] + 1 167 | end 168 | end 169 | return (concat(t)) 170 | end 171 | 172 | local hsalsa20 = function(key, counter, nonce) 173 | assert(#key == 32, "#key must be 32") 174 | assert(#nonce == 8, "#nonce must be 8") 175 | local keya = table.pack(string.unpack("> 32} 178 | local stream = hsalsa20_block(keya, countera, noncea) 179 | return string.pack(pat8, table.unpack(stream)) 180 | end 181 | 182 | ------------------------------------------------------------ 183 | return { 184 | encrypt = salsa20_encrypt, 185 | decrypt = salsa20_encrypt, 186 | stream = salsa20_stream, 187 | hsalsa20 = hsalsa20, 188 | -- 189 | key_size = 32, 190 | nonce_size = 8, 191 | } 192 | 193 | --end of salsa20 194 | -------------------------------------------------------------------------------- /plc/sha2.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2018 Phil Leblanc -- see LICENSE file 2 | ------------------------------------------------------------------------ 3 | 4 | -- SHA2-256 and SHA2-512 -- see RFC 6234 5 | 6 | 7 | -- sha2-256 initially based on code written by Roberto Ierusalimschy 8 | -- for an early Lua 5.3rc with (un)packint() functions. 9 | -- published by Roberto on the Lua mailing list 10 | -- http://lua-users.org/lists/lua-l/2014-03/msg00851.html 11 | -- can be distributed under the MIT License terms. see: 12 | -- http://lua-users.org/lists/lua-l/2014-08/msg00628.html 13 | -- 14 | -- adapted to 5.3 (string.(un)pack()) --phil, 150827 15 | -- 16 | -- optimized for performance, 181008. The core permutation 17 | -- for sha2-256 and sha2-512 is lifted from the very good 18 | -- implementation by Egor Skriptunoff, also MIT-licensed. See 19 | -- https://github.com/Egor-Skriptunoff/pure_lua_SHA2 20 | 21 | 22 | ------------------------------------------------------------ 23 | -- local declarations 24 | 25 | local string, assert = string, assert 26 | local spack, sunpack = string.pack, string.unpack 27 | 28 | ------------------------------------------------------------------------ 29 | -- sha256 30 | 31 | -- Initialize table of round constants 32 | -- (first 32 bits of the fractional parts of the cube roots of the first 33 | -- 64 primes 2..311) 34 | local k256 = { 35 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 36 | 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 37 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 38 | 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 39 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 40 | 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 41 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 42 | 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 43 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 44 | 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 45 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 46 | 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 47 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 48 | 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 49 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 50 | 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, 51 | } 52 | 53 | local function pad64(msg, len) 54 | local extra = 64 - ((len + 1 + 8) % 64) 55 | len = spack(">I8", len * 8) -- original len in bits, coded 56 | msg = msg .. "\128" .. string.rep("\0", extra) .. len 57 | assert(#msg % 64 == 0) 58 | return msg 59 | end 60 | 61 | local ww256 = {} 62 | 63 | local function sha256 (msg) 64 | msg = pad64(msg, #msg) 65 | local h1, h2, h3, h4, h5, h6, h7, h8 = 66 | 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 67 | 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 68 | local k = k256 69 | local w = ww256 70 | local mlen = #msg 71 | -- Process the message in successive 512-bit (64 bytes) chunks: 72 | for i = 1, mlen, 64 do 73 | w[1], w[2], w[3], w[4], w[5], w[6], w[7], w[8], 74 | w[9], w[10], w[11], w[12], w[13], w[14], w[15], w[16] 75 | = sunpack(">I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4I4", msg, i) 76 | -- mix msg block in state 77 | for j = 17, 64 do 78 | local x = w[j - 15]; x = (x << 32) | x 79 | local y = w[j - 2]; y = (y << 32) | y 80 | w[j] = ( ((x >> 7) ~ (x >> 18) ~ (x >> 35)) 81 | + ((y >> 17) ~ (y >> 19) ~ (y >> 42)) 82 | + w[j - 7] + w[j - 16] ) & 0xffffffff 83 | end 84 | local a, b, c, d, e, f, g, h = h1, h2, h3, h4, h5, h6, h7, h8 85 | -- main state permutation 86 | for j = 1, 64 do 87 | e = (e << 32) | (e & 0xffffffff) 88 | local t1 = ((e >> 6) ~ (e >> 11) ~ (e >> 25)) 89 | + (g ~ e & (f ~ g)) + h + k[j] + w[j] 90 | h = g 91 | g = f 92 | f = e 93 | e = (d + t1) 94 | d = c 95 | c = b 96 | b = a 97 | a = (a << 32) | (a & 0xffffffff) 98 | a = t1 + ((a ~ c) & d ~ a & c) 99 | + ((a >> 2) ~ (a >> 13) ~ (a >> 22)) 100 | end 101 | h1 = h1 + a 102 | h2 = h2 + b 103 | h3 = h3 + c 104 | h4 = h4 + d 105 | h5 = h5 + e 106 | h6 = h6 + f 107 | h7 = h7 + g 108 | h8 = h8 + h 109 | end 110 | -- clamp hash to 32-bit words 111 | h1 = h1 & 0xffffffff 112 | h2 = h2 & 0xffffffff 113 | h3 = h3 & 0xffffffff 114 | h4 = h4 & 0xffffffff 115 | h5 = h5 & 0xffffffff 116 | h6 = h6 & 0xffffffff 117 | h7 = h7 & 0xffffffff 118 | h8 = h8 & 0xffffffff 119 | -- return hash as a binary string 120 | return spack(">I4I4I4I4I4I4I4I4", h1, h2, h3, h4, h5, h6, h7, h8) 121 | end --sha256 122 | 123 | ------------------------------------------------------------------------ 124 | -- sha512 125 | 126 | local k512 = { 127 | 0x428a2f98d728ae22,0x7137449123ef65cd,0xb5c0fbcfec4d3b2f,0xe9b5dba58189dbbc, 128 | 0x3956c25bf348b538,0x59f111f1b605d019,0x923f82a4af194f9b,0xab1c5ed5da6d8118, 129 | 0xd807aa98a3030242,0x12835b0145706fbe,0x243185be4ee4b28c,0x550c7dc3d5ffb4e2, 130 | 0x72be5d74f27b896f,0x80deb1fe3b1696b1,0x9bdc06a725c71235,0xc19bf174cf692694, 131 | 0xe49b69c19ef14ad2,0xefbe4786384f25e3,0x0fc19dc68b8cd5b5,0x240ca1cc77ac9c65, 132 | 0x2de92c6f592b0275,0x4a7484aa6ea6e483,0x5cb0a9dcbd41fbd4,0x76f988da831153b5, 133 | 0x983e5152ee66dfab,0xa831c66d2db43210,0xb00327c898fb213f,0xbf597fc7beef0ee4, 134 | 0xc6e00bf33da88fc2,0xd5a79147930aa725,0x06ca6351e003826f,0x142929670a0e6e70, 135 | 0x27b70a8546d22ffc,0x2e1b21385c26c926,0x4d2c6dfc5ac42aed,0x53380d139d95b3df, 136 | 0x650a73548baf63de,0x766a0abb3c77b2a8,0x81c2c92e47edaee6,0x92722c851482353b, 137 | 0xa2bfe8a14cf10364,0xa81a664bbc423001,0xc24b8b70d0f89791,0xc76c51a30654be30, 138 | 0xd192e819d6ef5218,0xd69906245565a910,0xf40e35855771202a,0x106aa07032bbd1b8, 139 | 0x19a4c116b8d2d0c8,0x1e376c085141ab53,0x2748774cdf8eeb99,0x34b0bcb5e19b48a8, 140 | 0x391c0cb3c5c95a63,0x4ed8aa4ae3418acb,0x5b9cca4f7763e373,0x682e6ff3d6b2b8a3, 141 | 0x748f82ee5defb2fc,0x78a5636f43172f60,0x84c87814a1f0ab72,0x8cc702081a6439ec, 142 | 0x90befffa23631e28,0xa4506cebde82bde9,0xbef9a3f7b2c67915,0xc67178f2e372532b, 143 | 0xca273eceea26619c,0xd186b8c721c0c207,0xeada7dd6cde0eb1e,0xf57d4f7fee6ed178, 144 | 0x06f067aa72176fba,0x0a637dc5a2c898a6,0x113f9804bef90dae,0x1b710b35131c471b, 145 | 0x28db77f523047d84,0x32caab7b40c72493,0x3c9ebe0a15c9bebc,0x431d67c49c100d4c, 146 | 0x4cc5d4becb3e42b6,0x597f299cfc657e2a,0x5fcb6fab3ad6faec,0x6c44198c4a475817 147 | } 148 | 149 | local function pad128(msg, len) 150 | local extra = 128 - ((len + 1 + 8) % 128) 151 | len = spack(">I8", len * 8) -- original len in bits, coded 152 | msg = msg .. "\128" .. string.rep("\0", extra) .. len 153 | assert(#msg % 128 == 0) 154 | return msg 155 | end 156 | 157 | local ww512 = {} 158 | 159 | local function sha512 (msg) 160 | msg = pad128(msg, #msg) 161 | local h1, h2, h3, h4, h5, h6, h7, h8 = 162 | 0x6a09e667f3bcc908, 0xbb67ae8584caa73b, 163 | 0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1, 164 | 0x510e527fade682d1, 0x9b05688c2b3e6c1f, 165 | 0x1f83d9abfb41bd6b, 0x5be0cd19137e2179 166 | local k = k512 167 | local w = ww512 -- 80 * i64 state 168 | local mlen = #msg 169 | -- Process the message as 128-byte blocks: 170 | -- (this is borrowed to Egor Skriptunoff's pure_lua_SHA2 171 | -- https://github.com/Egor-Skriptunoff/pure_lua_SHA2) 172 | for i = 1, mlen, 128 do 173 | w[1], w[2], w[3], w[4], w[5], w[6], w[7], w[8], 174 | w[9], w[10], w[11], w[12], w[13], w[14], w[15], w[16] 175 | = sunpack(">i8i8i8i8i8i8i8i8i8i8i8i8i8i8i8i8", msg, i) 176 | -- mix msg block in state 177 | 178 | for j = 17, 80 do 179 | local a = w[j-15] 180 | local b = w[j-2] 181 | w[j] = (a >> 1 ~ a >> 7 ~ a >> 8 ~ a << 56 ~ a << 63) 182 | + (b >> 6 ~ b >> 19 ~ b >> 61 ~ b << 3 ~ b << 45) 183 | + w[j-7] + w[j-16] 184 | end 185 | local a, b, c, d, e, f, g, h = h1, h2, h3, h4, h5, h6, h7, h8 186 | -- main state permutation 187 | for j = 1, 80 do 188 | local z = (e >> 14 ~ e >> 18 ~ e >> 41 ~ e << 23 189 | ~ e << 46 ~ e << 50) 190 | + (g ~ e & (f ~ g)) + h + k[j] + w[j] 191 | h = g 192 | g = f 193 | f = e 194 | e = z + d 195 | d = c 196 | c = b 197 | b = a 198 | a = z + ((a ~ c) & d ~ a & c) 199 | + (a >> 28 ~ a >> 34 ~ a >> 39 ~ a << 25 200 | ~ a << 30 ~ a << 36) 201 | end 202 | h1 = h1 + a 203 | h2 = h2 + b 204 | h3 = h3 + c 205 | h4 = h4 + d 206 | h5 = h5 + e 207 | h6 = h6 + f 208 | h7 = h7 + g 209 | h8 = h8 + h 210 | end 211 | -- return hash as a binary string 212 | return spack(">i8i8i8i8i8i8i8i8", h1, h2, h3, h4, h5, h6, h7, h8) 213 | end --sha512 214 | 215 | ------------------------------------------------------------------------ 216 | 217 | return { 218 | sha256 = sha256, 219 | sha512 = sha512, 220 | } 221 | 222 | -------------------------------------------------------------------------------- /plc/sha3.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2014 Joseph Wallace 2 | -- Copyright (c) 2015 Phil Leblanc 3 | -- License: MIT - see LICENSE file 4 | ------------------------------------------------------------ 5 | 6 | -- 170612 SHA-3 padding fixed. 7 | -- (reported by Michael Rosenberg https://github.com/doomrobo) 8 | 9 | -- 150827 original code modified and optimized 10 | -- (more than 2x performance improvement for sha3-512) --phil 11 | 12 | -- Directly devived from a Keccak implementation by Joseph Wallace 13 | -- published on the Lua mailing list in 2014 14 | -- http://lua-users.org/lists/lua-l/2014-03/msg00905.html 15 | 16 | 17 | ------------------------------------------------------------ 18 | -- sha3 / keccak 19 | 20 | local char = string.char 21 | local concat = table.concat 22 | local spack, sunpack = string.pack, string.unpack 23 | 24 | -- the Keccak constants and functionality 25 | 26 | local ROUNDS = 24 27 | 28 | local roundConstants = { 29 | 0x0000000000000001, 30 | 0x0000000000008082, 31 | 0x800000000000808A, 32 | 0x8000000080008000, 33 | 0x000000000000808B, 34 | 0x0000000080000001, 35 | 0x8000000080008081, 36 | 0x8000000000008009, 37 | 0x000000000000008A, 38 | 0x0000000000000088, 39 | 0x0000000080008009, 40 | 0x000000008000000A, 41 | 0x000000008000808B, 42 | 0x800000000000008B, 43 | 0x8000000000008089, 44 | 0x8000000000008003, 45 | 0x8000000000008002, 46 | 0x8000000000000080, 47 | 0x000000000000800A, 48 | 0x800000008000000A, 49 | 0x8000000080008081, 50 | 0x8000000000008080, 51 | 0x0000000080000001, 52 | 0x8000000080008008 53 | } 54 | 55 | local rotationOffsets = { 56 | -- ordered for [x][y] dereferencing, so appear flipped here: 57 | {0, 36, 3, 41, 18}, 58 | {1, 44, 10, 45, 2}, 59 | {62, 6, 43, 15, 61}, 60 | {28, 55, 25, 21, 56}, 61 | {27, 20, 39, 8, 14} 62 | } 63 | 64 | 65 | 66 | -- the full permutation function 67 | local function keccakF(st) 68 | local permuted = st.permuted 69 | local parities = st.parities 70 | for round = 1, ROUNDS do 71 | --~ local permuted = permuted 72 | --~ local parities = parities 73 | 74 | -- theta() 75 | for x = 1,5 do 76 | parities[x] = 0 77 | local sx = st[x] 78 | for y = 1,5 do parities[x] = parities[x] ~ sx[y] end 79 | end 80 | -- 81 | -- unroll the following loop 82 | --for x = 1,5 do 83 | -- local p5 = parities[(x)%5 + 1] 84 | -- local flip = parities[(x-2)%5 + 1] ~ ( p5 << 1 | p5 >> 63) 85 | -- for y = 1,5 do st[x][y] = st[x][y] ~ flip end 86 | --end 87 | local p5, flip, s 88 | --x=1 89 | p5 = parities[2] 90 | flip = parities[5] ~ (p5 << 1 | p5 >> 63) 91 | s = st[1] 92 | for y = 1,5 do s[y] = s[y] ~ flip end 93 | --x=2 94 | p5 = parities[3] 95 | flip = parities[1] ~ (p5 << 1 | p5 >> 63) 96 | s = st[2] 97 | for y = 1,5 do s[y] = s[y] ~ flip end 98 | --x=3 99 | p5 = parities[4] 100 | flip = parities[2] ~ (p5 << 1 | p5 >> 63) 101 | s = st[3] 102 | for y = 1,5 do s[y] = s[y] ~ flip end 103 | --x=4 104 | p5 = parities[5] 105 | flip = parities[3] ~ (p5 << 1 | p5 >> 63) 106 | s = st[4] 107 | for y = 1,5 do s[y] = s[y] ~ flip end 108 | --x=5 109 | p5 = parities[1] 110 | flip = parities[4] ~ (p5 << 1 | p5 >> 63) 111 | s = st[5] 112 | for y = 1,5 do s[y] = s[y] ~ flip end 113 | 114 | -- rhopi() 115 | for y = 1,5 do 116 | local py = permuted[y] 117 | local r 118 | for x = 1,5 do 119 | s, r = st[x][y], rotationOffsets[x][y] 120 | py[(2*x + 3*y)%5 + 1] = (s << r | s >> (64-r)) 121 | end 122 | end 123 | 124 | -- chi() - unroll the loop 125 | --for x = 1,5 do 126 | -- for y = 1,5 do 127 | -- local combined = (~ permuted[(x)%5 +1][y]) & permuted[(x+1)%5 +1][y] 128 | -- st[x][y] = permuted[x][y] ~ combined 129 | -- end 130 | --end 131 | 132 | local p, p1, p2 133 | --x=1 134 | s, p, p1, p2 = st[1], permuted[1], permuted[2], permuted[3] 135 | for y = 1,5 do s[y] = p[y] ~ (~ p1[y]) & p2[y] end 136 | --x=2 137 | s, p, p1, p2 = st[2], permuted[2], permuted[3], permuted[4] 138 | for y = 1,5 do s[y] = p[y] ~ (~ p1[y]) & p2[y] end 139 | --x=3 140 | s, p, p1, p2 = st[3], permuted[3], permuted[4], permuted[5] 141 | for y = 1,5 do s[y] = p[y] ~ (~ p1[y]) & p2[y] end 142 | --x=4 143 | s, p, p1, p2 = st[4], permuted[4], permuted[5], permuted[1] 144 | for y = 1,5 do s[y] = p[y] ~ (~ p1[y]) & p2[y] end 145 | --x=5 146 | s, p, p1, p2 = st[5], permuted[5], permuted[1], permuted[2] 147 | for y = 1,5 do s[y] = p[y] ~ (~ p1[y]) & p2[y] end 148 | 149 | -- iota() 150 | st[1][1] = st[1][1] ~ roundConstants[round] 151 | end 152 | end 153 | 154 | 155 | local function absorb(st, buffer) 156 | 157 | local blockBytes = st.rate / 8 158 | local blockWords = blockBytes / 8 159 | 160 | -- append 0x01 byte and pad with zeros to block size (rate/8 bytes) 161 | local totalBytes = #buffer + 1 162 | -- for keccak (2012 submission), the padding is byte 0x01 followed by zeros 163 | -- for SHA3 (NIST, 2015), the padding is byte 0x06 followed by zeros 164 | 165 | -- Keccak: 166 | -- buffer = buffer .. ( '\x01' .. char(0):rep(blockBytes - (totalBytes % blockBytes))) 167 | 168 | -- SHA3: 169 | buffer = buffer .. ( '\x06' .. char(0):rep(blockBytes - (totalBytes % blockBytes))) 170 | totalBytes = #buffer 171 | 172 | --convert data to an array of u64 173 | local words = {} 174 | for i = 1, totalBytes - (totalBytes % 8), 8 do 175 | words[#words + 1] = sunpack('> (64 - n)) 39 | end 40 | 41 | 42 | -- the default function is SipHash-2-4 43 | local cROUNDS = 2 -- number of compression rounds 44 | local dROUNDS = 4 -- number of diffusion rounds 45 | 46 | local function siphash_core(ins, k0, k1, flag16) 47 | -- k0, k1: 16-byte key as 2 integers 48 | -- return result as one (if flag16 is false) or two integers 49 | local v0 = 0x736f6d6570736575 50 | local v1 = 0x646f72616e646f6d 51 | local v2 = 0x6c7967656e657261 52 | local v3 = 0x7465646279746573 53 | local inslen = #ins 54 | local left = inslen & 7 55 | local b = (#ins) << 56 56 | local ii = 1 57 | local m 58 | local function sipround(v0, v1, v2, v3) 59 | v0 = v0 + v1 60 | v1 = (v1 << 13) | (v1 >> 51) -- 64-13 61 | v1 = v1 ~ v0 62 | v0 = (v0 << 32) | (v0 >> 32) -- 64-32 63 | v2 = v2 + v3 64 | v3 = (v3 << 16) | (v3 >> 48) -- 64-16 65 | v3 = v3 ~ v2 66 | v0 = v0 + v3 67 | v3 = (v3 << 21) | (v3 >> 43) -- 64-21 68 | v3 = v3 ~ v0 69 | v2 = v2 + v1 70 | v1 = (v1 << 17) | (v1 >> 47) -- 64-17 71 | v1 = v1 ~ v2 72 | v2 = (v2 << 32) | (v2 >> 32) -- 64-32 73 | return v0, v1, v2, v3 74 | end 75 | --[[ (same output as the TRACE macro in the C reference implementation) 76 | local function trace() 77 | pf("(%d) v0 %08x %08x", inslen, v0>>32, v0 & 0xffffffff) 78 | pf("(%d) v1 %08x %08x", inslen, v1>>32, v1 & 0xffffffff) 79 | pf("(%d) v2 %08x %08x", inslen, v2>>32, v2 & 0xffffffff) 80 | pf("(%d) v3 %08x %08x", inslen, v3>>32, v3 & 0xffffffff) 81 | end 82 | --]] 83 | v3 = v3 ~ k1 84 | v2 = v2 ~ k0 85 | v1 = v1 ~ k1 86 | v0 = v0 ~ k0 87 | --trace() 88 | if flag16 then v1 = v1 ~ 0xee end 89 | while inslen - ii >= 7 do 90 | m, ii = sunpack(" 0 then b = b | (byte(ins, ii) ) end 99 | if left > 1 then b = b | (byte(ins, ii+1) << 8) end 100 | if left > 2 then b = b | (byte(ins, ii+2) << 16) end 101 | if left > 3 then b = b | (byte(ins, ii+3) << 24) end 102 | if left > 4 then b = b | (byte(ins, ii+4) << 32) end 103 | if left > 5 then b = b | (byte(ins, ii+5) << 40) end 104 | if left > 6 then b = b | (byte(ins, ii+6) << 48) end 105 | v3 = v3 ~ b 106 | --trace() 107 | for i = 1, cROUNDS do v0, v1, v2, v3 = sipround(v0, v1, v2, v3) end 108 | v0 = v0 ~ b 109 | v2 = v2 ~ (flag16 and 0xee or 0xff) 110 | --trace() 111 | for i = 1, dROUNDS do v0, v1, v2, v3 = sipround(v0, v1, v2, v3) end 112 | 113 | local r1, r2 114 | r1 = v0 ~ v1 ~ v2 ~ v3 115 | if not flag16 then return r1, nil end 116 | v1 = v1 ~ 0xdd 117 | --trace() 118 | for i = 1, dROUNDS do v0, v1, v2, v3 = sipround(v0, v1, v2, v3) end 119 | r2 = v0 ~ v1 ~ v2 ~ v3 120 | return r1, r2 121 | end--siphash_core() 122 | 123 | local function siphash(ins, ks, flag16) 124 | -- return hash of string ins 125 | -- optional key is string ks (must be 16 bytes) 126 | -- if flag16, return a 16-byte hash string (binary - no hex encoding) 127 | -- else (default) return a 8-byte hash string 128 | ks = ks or 129 | "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" 130 | local k0, k1 -- the MAC key as 2 8-byte integers 131 | if ks then 132 | assert(#ks == 16, "key must be a 16-byte string") 133 | k0, k1 = sunpack("I4I4I4I4', key) 57 | local skt0 = {} -- the "sum + key[sum&3]" table 58 | local skt1 = {} -- the "sum + key[sum>>11 & 3]" table 59 | local sum, delta = 0, 0x9E3779B9 60 | for i = 1, ROUNDS do 61 | skt0[i] = sum + kt[(sum & 3) + 1] 62 | sum = (sum + delta) & 0xffffffff 63 | skt1[i] = sum + kt[((sum>>11) & 3) + 1] 64 | end 65 | return { skt0=skt0, skt1=skt1, } 66 | end 67 | 68 | local function encrypt_u64(st, bu) 69 | -- st: state, produced by keysetup 70 | -- ub: 64-bit block as a uint64 (big endian - unpack with ">I8") 71 | local skt0, skt1 = st.skt0, st.skt1 72 | -- xtea works on big endian numbers: v0 is MSB, v1 is LSB 73 | local v0, v1 = bu >> 32, bu & 0xffffffff 74 | --~ p8(v0);p8(v1);p8(b) 75 | local sum, delta = 0, 0x9E3779B9 76 | for i = 1, ROUNDS do 77 | v0 = (v0 + ((((v1<<4) ~ (v1>>5)) + v1) ~ skt0[i])) & 0xffffffff 78 | v1 = (v1 + ((((v0<<4) ~ (v0>>5)) + v0) ~ skt1[i])) & 0xffffffff 79 | end 80 | bu = (v0 << 32) | v1 81 | return bu 82 | end 83 | 84 | local function decrypt_u64(st, bu) 85 | -- st: state, produced by keysetup 86 | -- bu: 64-bit encrypted block as a uint64 (big endian) 87 | -- returns decrypted block as a uint64 88 | local skt0, skt1 = st.skt0, st.skt1 89 | local v0, v1 = bu >> 32, bu & 0xffffffff 90 | local sum, delta = 0, 0x9E3779B9 91 | for i = ROUNDS, 1, -1 do 92 | v1 = (v1 - ((((v0<<4) ~ (v0>>5)) + v0) ~ skt1[i])) & 0xffffffff 93 | v0 = (v0 - ((((v1<<4) ~ (v1>>5)) + v1) ~ skt0[i])) & 0xffffffff 94 | end 95 | bu = (v0 << 32) | v1 96 | return bu 97 | end 98 | 99 | -- to encrypt/decrypt an 8-byte string (eg. for ECB mode), 100 | -- use the following: 101 | -- encrypt: spack(">I8", encrypt_u64(st, sunpack(">I8", b))) 102 | -- decrypt: spack(">I8", decrypt_u64(st, sunpack(">I8", b))) 103 | 104 | -- convenience functions: same core encryption/decryption 105 | -- but the block parameter is a 8-byte string instead of a uint64 106 | -- [hmm, not sure I will keep these two...] 107 | 108 | local function encrypt_s8(st, b) 109 | return spack(">I8", encrypt_u64(st, (sunpack(">I8", b)))) 110 | end 111 | 112 | local function decrypt_s8(st, b) 113 | return spack(">I8", decrypt_u64(st, (sunpack(">I8", b)))) 114 | end 115 | 116 | 117 | ------------------------------------------------------------------------ 118 | -- stream encryption 119 | 120 | local function xtea_ctr(key, iv, itxt) 121 | -- encrypt/decrypt a text (stream en/decryption in CTR mode 122 | -- (the counter is the index in the input text. It is XORed 123 | -- with the IV at each block ) 124 | -- key is a 16-byte string 125 | -- iv is a 8-byte string 126 | -- itxt is the text to encrypt/decrypt 127 | -- returns the encrypted/decrypted text 128 | -- 129 | assert(#key == 16, "bad key length") 130 | assert(#iv == 8, "bad IV length") 131 | -- special case: empty string 132 | if #itxt == 0 then return "" end 133 | local ivu = sunpack("= 5.3" } 24 | 25 | build = { 26 | type = "builtin", 27 | modules = { 28 | ["plc.aead_chacha_poly"] = "plc/aead_chacha_poly.lua", 29 | ["plc.base58"] = "plc/base58.lua", 30 | ["plc.base58"] = "plc/base58.lua", 31 | ["plc.bin"] = "plc/bin.lua", 32 | ["plc.blake2b"] = "plc/blake2b.lua", 33 | ["plc.box"] = "plc/box.lua", 34 | ["plc.chacha20"] = "plc/chacha20.lua", 35 | ["plc.checksum"] = "plc/checksum.lua", 36 | ["plc.ec25519"] = "plc/ec25519.lua", 37 | ["plc.md5"] = "plc/md5.lua", 38 | ["plc.norx"] = "plc/norx.lua", 39 | ["plc.norx32"] = "plc/norx32.lua", 40 | ["plc.poly1305"] = "plc/poly1305.lua", 41 | ["plc.rabbit"] = "plc/rabbit.lua", 42 | ["plc.rc4"] = "plc/rc4.lua", 43 | ["plc.salsa20"] = "plc/salsa20.lua", 44 | ["plc.sha2"] = "plc/sha2.lua", 45 | ["plc.sha3"] = "plc/sha3.lua", 46 | ["plc.xtea"] = "plc/xtea.lua", 47 | }, 48 | copy_directories = {}, 49 | } 50 | -------------------------------------------------------------------------------- /rockspec/plc-scm-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "plc" 2 | version = "scm-1" 3 | 4 | source = { 5 | url = "git://github.com/philanc/plc.git", 6 | } 7 | 8 | description = { 9 | summary = "Pure Lua Crypto", 10 | detailed = [[ 11 | A pure Lua cryptograhy library. 12 | ]], 13 | homepage = "http://github.com/philanc/plc", 14 | license = "MIT/X11", 15 | } 16 | 17 | dependencies = { "lua >= 5.3" } 18 | 19 | build = { 20 | type = "builtin", 21 | modules = { 22 | ["plc.aead_chacha_poly"] = "plc/aead_chacha_poly.lua", 23 | ["plc.base58"] = "plc/base58.lua", 24 | ["plc.base64"] = "plc/base64.lua", 25 | ["plc.base85"] = "plc/base85.lua", 26 | ["plc.bin"] = "plc/bin.lua", 27 | ["plc.blake2b"] = "plc/blake2b.lua", 28 | ["plc.box"] = "plc/box.lua", 29 | ["plc.chacha20"] = "plc/chacha20.lua", 30 | ["plc.checksum"] = "plc/checksum.lua", 31 | ["plc.ec25519"] = "plc/ec25519.lua", 32 | ["plc.gimli"] = "plc/gimli.lua", 33 | ["plc.md5"] = "plc/md5.lua", 34 | ["plc.morus"] = "plc/morus.lua", 35 | ["plc.norx"] = "plc/norx.lua", 36 | ["plc.norx32"] = "plc/norx32.lua", 37 | ["plc.poly1305"] = "plc/poly1305.lua", 38 | ["plc.rabbit"] = "plc/rabbit.lua", 39 | ["plc.rc4"] = "plc/rc4.lua", 40 | ["plc.salsa20"] = "plc/salsa20.lua", 41 | ["plc.sha2"] = "plc/sha2.lua", 42 | ["plc.sha3"] = "plc/sha3.lua", 43 | ["plc.siphash"] = "plc/siphash.lua", 44 | ["plc.xtea"] = "plc/xtea.lua", 45 | }, 46 | copy_directories = {}, 47 | } 48 | -------------------------------------------------------------------------------- /test/test_aead_chacha_poly.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2015 Phil Leblanc -- see LICENSE file 2 | ------------------------------------------------------------ 3 | 4 | local aead = require "plc.aead_chacha_poly" 5 | 6 | 7 | local function test_aead_encrypt() 8 | -- 9 | -- test poly_keygen() -- rfc, sect 2.6.2 10 | -- 11 | local key, nonce, expected, aad, iv, const, plain, exptag, encr, res, tag 12 | key = "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" 13 | .. "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" 14 | nonce = "\x00\x00\x00\x00\x00\x01\x02\x03\x04\x05\x06\x07" 15 | expected = "\x8a\xd5\xa0\x8b\x90\x5f\x81\xcc\x81\x50\x40\x27\x4a\xb2\x94\x71" 16 | .. "\xa8\x33\xb6\x37\xe3\xfd\x0d\xa5\x08\xdb\xb8\xe2\xfd\xd1\xa6\x46" 17 | assert(aead.poly_keygen(key, nonce) == expected) 18 | -- 19 | -- test aead_encrypt() -- rfc, sect 2.8.2 20 | -- 21 | key = "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f" 22 | .. "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f" 23 | aad = "\x50\x51\x52\x53\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" 24 | iv = "\x40\x41\x42\x43\x44\x45\x46\x47" 25 | const = "\x07\x00\x00\x00" 26 | plain = "Ladies and Gentlemen of the class of '99: If I could offer you only " 27 | .. "one tip for the future, sunscreen would be it." 28 | exptag = "\x1a\xe1\x0b\x59\x4f\x09\xe2\x6a\x7e\x90\x2e\xcb\xd0\x60\x06\x91" 29 | encr, tag = aead.encrypt(aad, key, iv, const, plain) 30 | assert(#plain == #encr) 31 | assert( tag == exptag ) 32 | res = aead.decrypt(aad, key, iv, const, encr, tag) 33 | assert(res == plain) 34 | -- 35 | end -- test_aead_encrypt() 36 | 37 | 38 | test_aead_encrypt() 39 | print("test_aead_encrypt: ok") 40 | -------------------------------------------------------------------------------- /test/test_ascon.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2023 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------------------ 4 | 5 | --[[ 6 | 7 | === test_ascon (ascon128) 8 | 9 | Ascon128 - AEAD Encryption, HASH, MAC, PRF 10 | 11 | (test results computed with the Ascon C reference implementation) 12 | 13 | ]] 14 | 15 | local bin = require "plc.bin" 16 | local stx, xts = bin.stohex, bin.hextos 17 | 18 | local ascon = require "plc.ascon" 19 | 20 | local encrypt = ascon.aead_encrypt 21 | local decrypt = ascon.aead_decrypt 22 | local hash = ascon.hash 23 | local mac = ascon.mac 24 | local prf = ascon.prf 25 | 26 | 27 | local function test_encrypt() 28 | local h, k, n, m, e, m2, err, ad 29 | -- AEAD encryption 30 | -- 16-byte key, 16-byte nonce, 16-byte auth tag 31 | k = "abcdefghijklmnop" 32 | n = "nnnnnnnnnnnnnnnn" 33 | m = ""; ad = nil -- default to empty string 34 | e = encrypt(k, n, m, ad) 35 | assert(e == xts[[bccf97f50336090c0c7215716c9d4766]]) 36 | m2 = decrypt(k,n,e,ad); assert(m2 == m) 37 | m = ""; ad = "A" 38 | e = encrypt(k, n, m, ad) 39 | assert(e == xts[[289848b6adb7b0ba1850cd6d586914b4]]) 40 | m2 = decrypt(k,n,e,ad); assert(m2 == m) 41 | m = "a"; ad = "" 42 | e = encrypt(k, n, m, ad) 43 | assert(e == xts[[2067a522ad5c4409cf78e9c684da3435e2]]) 44 | m2 = decrypt(k,n,e,ad); assert(m2 == m) 45 | m = "abcdefghi"; ad = "" 46 | e = encrypt(k, n, m, ad) 47 | assert(e == xts[[ 48 | 20945fc794adf584aa64ca1c510802bb123ba77715ad24ef60]]) 49 | m2 = decrypt(k,n,e,ad); assert(m2 == m) 50 | m = "abcdefgh"; ad = "12345678" -- just one block(m,ad) 51 | e = encrypt(k, n, m, ad) 52 | assert(e == xts[[ 53 | 32ed6d7145bf3c4454a4367ed9e5952990fac87173e0ad1d]]) 54 | m2 = decrypt(k,n,e,ad); assert(m2 == m) 55 | m = "abcdefghij"; ad = "1234567890" -- more than one block (m,ad) 56 | e = encrypt(k, n, m, ad) 57 | assert(e == xts[[ 58 | 4e31ada9034dea60ea2263495305a9fdee6093dee3836ea24a2c]]) 59 | m2 = decrypt(k,n,e,ad); assert(m2 == m) 60 | 61 | end--test_encrypt() 62 | 63 | local function test_hash() 64 | h = hash("ascon") 65 | assert(h == xts[[ 66 | 02c895cb92d79f195ed9e3e2af89ae30 67 | 7059104aaa819b9a987a76cf7cf51e6e 68 | ]]) 69 | h = mac("abcdefghijklmnop", "ascon") 70 | assert(h == xts"5829fd289ae4b3ccba504f803ef3cee3") 71 | h = prf("abcdefghijklmnop", "ascon") 72 | assert(h == xts"7d7453dc0272ef2100de117484ccfa7a") 73 | h = prf("abcdefghijklmnop", "ascon", 23) 74 | assert(h == xts"7d7453dc0272ef2100de117484ccfa7abd101a261e8bf7") 75 | 76 | end--test_hash() 77 | 78 | test_encrypt() 79 | test_hash() 80 | 81 | print("test_ascon: ok") 82 | -------------------------------------------------------------------------------- /test/test_base58.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2015 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------ 4 | -- base58 tests 5 | 6 | local base58 = require "plc.base58" 7 | 8 | local function base58test() 9 | 10 | assert(base58.encode('\x01') == '2') 11 | assert(base58.encode('\x00\x01') == '12') 12 | assert(base58.encode('') == '') 13 | assert(base58.encode('\0\0') == '11') 14 | assert(base58.encode('o hai') == 'DYB3oMS') --[1] 15 | local x1 = "\x00\x01\x09\x66\x77\x60\x06\x95\x3D\x55\x67\x43" 16 | .. "\x9E\x5E\x39\xF8\x6A\x0D\x27\x3B\xEE\xD6\x19\x67\xF6" 17 | local e1 = "16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM" --[2] 18 | assert(base58.encode(x1) == e1) 19 | local x2 = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" 20 | .. "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" 21 | local e2 = "thX6LZfHDZZKUs92febYZhYRcXddmzfzF2NvTkPNE" --[3] 22 | assert(base58.encode(x2) == e2) 23 | 24 | assert(base58.decode('') == '') 25 | assert(base58.decode('11') == '\0\0') 26 | assert(base58.decode('DYB3oMS') == 'o hai') 27 | assert(base58.decode(e1) == x1) 28 | assert(base58.decode(e2) == x2) 29 | 30 | end--base58test() 31 | 32 | base58test() 33 | print("test_base58: ok") 34 | -------------------------------------------------------------------------------- /test/test_base64.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2017 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------ 4 | -- base64 tests 5 | 6 | local base64 = require "plc.base64" 7 | local be = base64.encode 8 | local bd = base64.decode 9 | 10 | local function base64test() 11 | assert(be"" == "") 12 | assert(be"a" == "YQ==") 13 | assert(be"aa" == "YWE=") 14 | assert(be"aaa" == "YWFh") 15 | assert(be"aaaa" == "YWFhYQ==") 16 | assert(be(("a"):rep(61)) == 17 | "YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFh" 18 | .. "YWFhYWFhYWFhYWFhYWFh\nYWFhYWFhYQ==") 19 | assert("" == bd"") 20 | assert("a" == bd"YQ==") 21 | assert("aa" == bd"YWE=") 22 | assert("aaa" == bd"YWFh") 23 | assert("aaaa" == bd"YWFhYQ==") 24 | assert(bd"YWFhYWFhYQ" == "aaaaaaa") 25 | assert(bd"YWF\nhY W\t\r\nFhYQ" == "aaaaaaa") 26 | assert(bd(be"\x00\x01\x02\x03\x00" ) == "\x00\x01\x02\x03\x00") 27 | 28 | -- 171103 - add a test for the "n*54-1" bug 29 | -- (strings with n*54-1 or n*54-2 bytes) 30 | for i = 0, 3 do 31 | local ln = 3 * 54 - i 32 | local s = ("a"):rep(ln) 33 | assert(bd(be(s)) == s) 34 | end 35 | 36 | end 37 | 38 | 39 | base64test() 40 | 41 | print("test_base64: ok") 42 | -------------------------------------------------------------------------------- /test/test_base85.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2017 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------ 4 | -- base85 (Z85) tests 5 | 6 | z85 = require("plc.base85") 7 | 8 | local s, m, e, s2, st 9 | 10 | -- test string from https://rfc.zeromq.org/spec:32/Z85/ 11 | s = "\x86\x4F\xD2\x6F\xB5\x59\xF7\x5B" 12 | e = z85.encode(s) 13 | assert(e == "HelloWorld") 14 | 15 | s2 = z85.decode(e) 16 | assert(s2 == s) 17 | 18 | -- test string from https://github.com/zeromq/rfc/blob/master/src/spec_32.c 19 | s = "\x8E\x0B\xDD\x69\x76\x28\xB9\x1D" .. 20 | "\x8F\x24\x55\x87\xEE\x95\xC5\xB0" .. 21 | "\x4D\x48\x96\x3F\x79\x25\x98\x77" .. 22 | "\xB4\x9C\xD9\x06\x3A\xEA\xD3\xB7" 23 | e = z85.encode(s) 24 | assert(e == "JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6") 25 | s2 = z85.decode(e) 26 | assert(s2 == s) 27 | 28 | s, m = z85.decode"abc" 29 | assert(not s and m == "invalid length") 30 | s, m = z85.decode[[abc"']] 31 | assert(not s and m == "invalid char") 32 | 33 | print("test_base85: ok") 34 | -------------------------------------------------------------------------------- /test/test_bin.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2015 Phil Leblanc -- see LICENSE file 2 | ------------------------------------------------------------------------ 3 | 4 | -- test bin.lua 5 | 6 | local bin = require "plc.bin" 7 | 8 | local stx = bin.stohex 9 | local xts = bin.hextos 10 | 11 | ------------------------------------------------------------------------ 12 | 13 | local function test_bin() 14 | local e 15 | -- 16 | -- test hex / string conversion 17 | local s13 = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d" 18 | local s26 = s13 .. s13 19 | assert(stx(s13) == "0102030405060708090a0b0c0d") 20 | assert(stx(s13, 16) == "0102030405060708090a0b0c0d") 21 | assert(stx(s13, 16, ':') == "01:02:03:04:05:06:07:08:09:0a:0b:0c:0d") 22 | assert(stx(s26) == 23 | "0102030405060708090a0b0c0d0102030405060708090a0b0c0d") 24 | assert(stx(s26, 16) == 25 | "0102030405060708090a0b0c0d010203\n0405060708090a0b0c0d") 26 | assert(stx(s26, 16, '+') == 27 | "01+02+03+04+05+06+07+08+09+0a+0b+0c+0d+" .. 28 | "01+02+03\n04+05+06+07+08+09+0a+0b+0c+0d" ) 29 | -- 30 | assert(xts("0102030405060708090a0b0c0d") == s13) 31 | -- ignore whitespace 32 | assert(xts(" 01020304050 \n 6070\t8090a0 b 0 c0d\n") == s13) 33 | -- with unsafe=true, invalid chars and whitespace are NOT ignored... 34 | assert(xts("01 02 030405060708090a0b0c0d", true) ~= s13) 35 | -- error cases 36 | -- (invalid chars) 37 | assert(not pcall(xts, "!!! 0102030405060708090a0b0c0d")) 38 | -- (invalid length, after whitespace removal) 39 | assert(not pcall(xts, "abc ")) 40 | -- 41 | -- test xor 42 | local pa5 = ('\xaa\x55'):rep(8) 43 | local p5a = ('\x55\xaa'):rep(8) 44 | local p00 = ('\x00\x00'):rep(8) 45 | local pff = ('\xff\xff'):rep(8) 46 | assert(bin.xor1(pa5, p00) == pa5) 47 | assert(bin.xor1(pa5, pff) == p5a) 48 | assert(bin.xor1(pa5, pa5) == p00) 49 | assert(bin.xor1(pa5, p5a) == pff) 50 | -- 51 | assert(bin.xor8(pa5, p00) == pa5) 52 | assert(bin.xor8(pa5, pff) == p5a) 53 | assert(bin.xor8(pa5, pa5) == p00) 54 | assert(bin.xor8(pa5, p5a) == pff) 55 | -- 56 | local function test_xor(xorfn, k, p) 57 | e = xorfn(k, p) 58 | --px(e) 59 | assert(#e == #p) 60 | assert(xorfn(k, e) == p) 61 | end 62 | test_xor(bin.xor1, ("k"):rep(1), ("a"):rep(1)) 63 | test_xor(bin.xor1, ("k"):rep(1), ("a"):rep(7)) 64 | test_xor(bin.xor1, ("k"):rep(3), ("a"):rep(8)) 65 | test_xor(bin.xor1, ("k"):rep(9), ("a"):rep(31)) 66 | test_xor(bin.xor1, ("k"):rep(1), ("a"):rep(7)) 67 | test_xor(bin.xor1, ("k"):rep(1), ("a"):rep(7)) 68 | 69 | test_xor(bin.xor8, ("k"):rep(8), ("a"):rep(1)) 70 | test_xor(bin.xor8, ("k"):rep(8), ("a"):rep(7)) 71 | test_xor(bin.xor8, ("k"):rep(8), ("a"):rep(8)) 72 | test_xor(bin.xor8, ("k"):rep(8), ("a"):rep(31)) 73 | test_xor(bin.xor8, ("k"):rep(16), ("a"):rep(7)) 74 | test_xor(bin.xor8, ("k"):rep(32), ("a"):rep(71)) 75 | -- 76 | return true 77 | end 78 | 79 | test_bin() 80 | 81 | print("test_bin: ok") 82 | -------------------------------------------------------------------------------- /test/test_blake2b.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2017 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------------------ 4 | -- blake2b tests 5 | 6 | local blake2b = require "plc.blake2b" 7 | 8 | local bin = require "plc.bin" -- for hex conversion 9 | 10 | local hextos = bin.hextos 11 | 12 | ------------------------------------------------------------------------ 13 | 14 | local function test_blake2b() 15 | local a, b 16 | -- 17 | a = blake2b.hash("") 18 | b = hextos[[ 19 | 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419 20 | d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce ]] 21 | assert(a == b) 22 | -- 23 | a = blake2b.hash("abc") 24 | b = hextos[[ 25 | ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d1 26 | 7d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923 ]] 27 | assert(a == b) 28 | -- 29 | a = blake2b.hash("The quick brown fox jumps over the lazy dog") 30 | b = hextos[[ 31 | a8add4bdddfd93e4877d2746e62817b116364a1fa7bc148d95090bc7333b3673 32 | f82401cf7aa2e4cb1ecd90296e3f14cb5413f8ed77be73045b13914cdcd6a918 ]] 33 | assert(a == b) 34 | -- 35 | a = blake2b.hash(('a'):rep(1000)) 36 | b = hextos[[ 37 | d6a69459fe93fc6b9537ed4336e5099e0dcca3e97290a412500ed7a0daffb03d 38 | 80cf3650a20e0591f748e10c3c534945ee83d5f2c9722f1a68d98b8c01af23fd ]] 39 | assert(a == b) 40 | -- 41 | -- test multiple updates 42 | local ctx 43 | ctx = blake2b.init() 44 | blake2b.update(ctx, ('a'):rep(500)) 45 | blake2b.update(ctx, ('a'):rep(500)) 46 | a = blake2b.final(ctx) 47 | assert(a == b) 48 | -- 49 | ctx = blake2b.init() 50 | for _ = 1, 100 do blake2b.update(ctx, ('a'):rep(10)) end 51 | a = blake2b.final(ctx) 52 | assert(a == b) 53 | -- 54 | ctx = blake2b.init() 55 | for _ = 1, 1000 do blake2b.update(ctx, 'a') end 56 | a = blake2b.final(ctx) 57 | assert(a == b) 58 | -- 59 | -- test shorter digest 60 | a = blake2b.hash("abc", 32) 61 | b = hextos[[ 62 | bddd813c634239723171ef3fee98579b94964e3bb1cb3e427262c8c068d52319 ]] 63 | assert(a == b) 64 | -- 65 | a = blake2b.hash("abc", 7) 66 | b = hextos[[ d5355e84cd8e92 ]] 67 | assert(a == b) 68 | -- 69 | -- test keyed digest 70 | a = blake2b.hash("abc", 64, "key key key") 71 | b = hextos[[ 72 | cd63b5bfd9d74769c604fc5e1b4d0486078511abc07ab748e6ae0e2654058354 73 | df6651c28d31396c71837d483fd1d1c5f9331fb23323495a6868361ad8196221 ]] 74 | assert(a == b) 75 | -- 76 | end --test_blake2b() 77 | 78 | 79 | test_blake2b() 80 | 81 | print("test_blake2b: ok") 82 | -------------------------------------------------------------------------------- /test/test_box.lua: -------------------------------------------------------------------------------- 1 | local box = require "plc.box" 2 | 3 | local function new_key() 4 | -- Note: we use math.random in this test because it is portable, 5 | -- but to generate a real key you should use a better RNG, for 6 | -- instance /dev/urandom on Linux. 7 | local t = {} 8 | for i = 1, 32 do t[i] = math.random(0, 255) end 9 | return string.char(table.unpack(t)) 10 | end 11 | 12 | local sk_a, sk_b = new_key(), new_key() 13 | local pk_a, pk_b = box.public_key(sk_a), box.public_key(sk_b) 14 | 15 | local k = ("k"):rep(32) 16 | local n = ("123456"):rep(4) 17 | local pt = "abc" 18 | 19 | do 20 | local et = assert(box.secretbox(pt, n, k)) 21 | local dt = assert(box.secretbox_open(et, n, k)) 22 | assert(dt == pt) 23 | end 24 | 25 | do 26 | local et = assert(box.box(pt, n, pk_b, sk_a)) 27 | local dt = assert(box.box_open(et, n, pk_a, sk_b)) 28 | assert(dt == pt) 29 | end 30 | 31 | print("test_box: ok") 32 | -------------------------------------------------------------------------------- /test/test_chacha20.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2018 Phil Leblanc -- see LICENSE file 2 | ------------------------------------------------------------------------ 3 | 4 | -- test chacha20.lua 5 | 6 | 7 | local chacha20 = require "plc.chacha20" 8 | 9 | local bin = require "plc.bin" 10 | local stx, xts = bin.stohex, bin.hextos 11 | 12 | local function px(s, msg) 13 | print("--", msg or "") 14 | print(stx(s, 16, " ")) 15 | end 16 | 17 | ------------------------------------------------------------------------ 18 | 19 | local function test_chacha20_encrypt() 20 | -- quick test with RFC 7539 test vector 21 | local plain = 22 | "Ladies and Gentlemen of the class of '99: If I could " 23 | .. "offer you only one tip for the future, sunscreen would be it." 24 | local key = 25 | "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" 26 | .. "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" 27 | local nonce = "\x00\x00\x00\x00\x00\x00\x00\x4a\x00\x00\x00\x00" 28 | assert(#key == chacha20.key_size) 29 | assert(#nonce == chacha20.nonce_size) 30 | local counter = 1 31 | local expected = 32 | "\x6e\x2e\x35\x9a\x25\x68\xf9\x80\x41\xba\x07\x28\xdd\x0d\x69\x81" 33 | .. "\xe9\x7e\x7a\xec\x1d\x43\x60\xc2\x0a\x27\xaf\xcc\xfd\x9f\xae\x0b" 34 | .. "\xf9\x1b\x65\xc5\x52\x47\x33\xab\x8f\x59\x3d\xab\xcd\x62\xb3\x57" 35 | .. "\x16\x39\xd6\x24\xe6\x51\x52\xab\x8f\x53\x0c\x35\x9f\x08\x61\xd8" 36 | .. "\x07\xca\x0d\xbf\x50\x0d\x6a\x61\x56\xa3\x8e\x08\x8a\x22\xb6\x5e" 37 | .. "\x52\xbc\x51\x4d\x16\xcc\xf8\x06\x81\x8c\xe9\x1a\xb7\x79\x37\x36" 38 | .. "\x5a\xf9\x0b\xbf\x74\xa3\x5b\xe6\xb4\x0b\x8e\xed\xf2\x78\x5e\x42" 39 | .. "\x87\x4d" 40 | local et = chacha20.encrypt(key, counter, nonce, plain) 41 | assert(et == expected) 42 | assert(plain == chacha20.encrypt(key, counter, nonce, et)) 43 | -- 44 | -- test some input lengths 45 | for i = 1, 66 do 46 | plain = ('p'):rep(i) 47 | et = chacha20.encrypt(key, counter, nonce, plain) 48 | local dt = chacha20.decrypt(key, counter, nonce, et) 49 | assert((#et == #plain) and (dt == plain)) 50 | end 51 | -- 52 | end 53 | 54 | local function test_hchacha20() 55 | -- test vectors from libsodium-1.0.16, xchacha20.c 56 | local k, n, sk, e 57 | k = xts[[ 58 | 24f11cce8a1b3d61e441561a696c1c1b7e173d084fd4812425435a8896a013dc ]] 59 | n = xts[[ d9660c5900ae19ddad28d6e06e45fe5e ]] 60 | assert( chacha20.hchacha20(k, n) == xts[[ 61 | 5966b3eec3bff1189f831f06afe4d4e3be97fa9235ec8c20d08acfbbb4e851e3 ]] ) 62 | -- 63 | k = xts[[ 64 | c49758f00003714c38f1d4972bde57ee8271f543b91e07ebce56b554eb7fa6a7 ]] 65 | n = xts[[ 31f0204e10cf4f2035f9e62bb5ba7303 ]] 66 | assert( chacha20.hchacha20(k, n) == xts[[ 67 | 0dd8cc400f702d2c06ed920be52048a287076b86480ae273c6d568a2e9e7518c ]] ) 68 | -- 69 | end 70 | 71 | local function test_xchacha20_encrypt() 72 | -- test from libsodium-1.0.16 73 | -- test/aead_xchacha20poly1305.c and aead_xchacha20poly1305.exp 74 | local k, ctr, n, m, c, e, m2 75 | k = xts[[ 76 | 808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f ]] 77 | ctr = 1 78 | n = xts[[ 07000000404142434445464748494a4b0000000000000000 ]] 79 | m = "Ladies and Gentlemen of the class of '99: If I could offer you " 80 | .. "only one tip for the future, sunscreen would be it." 81 | e = xts[[ 82 | 45 3c 06 93 a7 40 7f 04 ff 4c 56 ae db 17 a3 c0 83 | a1 af ff 01 17 49 30 fc 22 28 7c 33 db cf 0a c8 84 | b8 9a d9 29 53 0a 1b b3 ab 5e 69 f2 4c 7f 60 70 85 | c8 f8 40 c9 ab b4 f6 9f bf c8 a7 ff 51 26 fa ee 86 | bb b5 58 05 ee 9c 1c f2 ce 5a 57 26 32 87 ae c5 87 | 78 0f 04 ec 32 4c 35 14 12 2c fc 32 31 fc 1a 8b 88 | 71 8a 62 86 37 30 a2 70 2b b7 63 66 11 6b ed 09 89 | e0 fd 90 | ]] 91 | c = chacha20.xchacha20_encrypt(k, ctr, n, m) 92 | --px(c) 93 | assert(c == e) 94 | m2 = chacha20.xchacha20_decrypt(k, ctr, n, c) 95 | assert(m2 == m) 96 | -- 97 | -- test vectors from 98 | -- https://github.com/golang/crypto/blob/master/chacha20poly1305/ 99 | -- chacha20poly1305_vectors_test.go 100 | k = xts[[ 101 | 194b1190fa31d483c222ec475d2d6117710dd1ac19a6f1a1e8e894885b7fa631 ]] 102 | ctr = 1 103 | n = xts[[ 6b07ea26bb1f2d92e04207b447f2fd1dd2086b442a7b6852 ]] 104 | m = xts[[ 105 | f7e11b4d372ed7cb0c0e157f2f9488d8efea0f9bbe089a345f51bdc77e30d139 106 | 2813c5d22ca7e2c7dfc2e2d0da67efb2a559058d4de7a11bd2a2915e ]] 107 | e = xts[[ 108 | 25ae14585790d71d39a6e88632228a70b1f6a041839dc89a74701c06bfa7c4de 109 | 3288b7772cb2919818d95777ab58fe5480d6e49958f5d2481431014a 110 | ]] 111 | c = chacha20.xchacha20_encrypt(k, ctr, n, m) 112 | --px(c) 113 | assert(c == e) 114 | -- 115 | k = xts[[ 116 | a60e09cd0bea16f26e54b62b2908687aa89722c298e69a3a22cf6cf1c46b7f8a ]] 117 | ctr = 1 118 | n = xts[[ 92da9d67854c53597fc099b68d955be32df2f0d9efe93614 ]] 119 | m = xts[[ 120 | d266927ca40b2261d5a4722f3b4da0dd5bec74e103fab431702309fd0d0f1a25 121 | 9c767b956aa7348ca923d64c04f0a2e898b0670988b15e ]] 122 | e = xts[[ 123 | 9dd6d05832f6b4d7f555a5a83930d6aed5423461d85f363efb6c474b6c4c826 124 | 1b680dea393e24c2a3c8d1cc9db6df517423085833aa21f ]] 125 | c = chacha20.xchacha20_encrypt(k, ctr, n, m) 126 | --px(c) 127 | assert(c == e) 128 | -- 129 | end 130 | 131 | 132 | test_chacha20_encrypt() 133 | test_hchacha20() 134 | test_xchacha20_encrypt() 135 | 136 | print("test_chacha20: ok") 137 | -------------------------------------------------------------------------------- /test/test_checksum.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2015 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------ 4 | -- checksum tests 5 | 6 | local checksum = require "plc.checksum" 7 | 8 | 9 | local function test_checksum() 10 | -- test values at wikipedia 11 | -- checked with checksum on https://quickhash.com/ 12 | 13 | local sfox = "The quick brown fox jumps over the lazy dog" 14 | 15 | -- crc32 16 | assert(checksum.crc32(sfox) == 0x414fa339 ) 17 | -- 0x414fa339 matches crc32-b on quickhash.com, not crc32 (?!?) 18 | assert(checksum.crc32_nt(sfox) == 0x414fa339 ) 19 | 20 | -- adler32 21 | assert(checksum.adler32(sfox) == 0x5bdc0fda ) 22 | assert(checksum.adler32("Wikipedia") == 0x11e60398 ) 23 | end 24 | 25 | 26 | test_checksum() 27 | 28 | print("test_checksum: ok") 29 | -------------------------------------------------------------------------------- /test/test_ec25519.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2015 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------ 4 | --[[ 5 | 6 | test ec25519 module 7 | 8 | ]] 9 | 10 | local ec = require "plc.ec25519" 11 | 12 | local bin = require "plc.bin" 13 | local xts, stx = bin.hextos, bin.stohex 14 | 15 | ------------------------------------------------------------ 16 | 17 | local function verify(x, y) 18 | -- verify that x[i] == y[i] for all i 19 | local b = #x == #y 20 | for i = 1, #x do b = b and (x[i] == y[i]) end 21 | return b 22 | end 23 | 24 | ------------------------------------------------------------ 25 | local function test_scalarmult_base() 26 | local n1 = { 27 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 28 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 29 | } 30 | local n2 = { 31 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255, 32 | 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255 33 | } 34 | local n3 = { 35 | 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 36 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 37 | } 38 | local q = {} 39 | local qexp1 = { 40 | 164,224,146,146,182,81,194,120,185,119,44,86,159,95,169,187, 41 | 19,217,6,180,106,182,140,157,249,220,43,68,9,248,162,9 42 | } 43 | local qexp2 = { 44 | 132,124,13,44,55,82,52,243,101,230,96,149,81,135,163,115, 45 | 90,15,118,19,209,96,157,58,106,77,140,83,174,170,90,34 46 | } 47 | local qexp3 = { 48 | 47,229,125,163,71,205,98,67,21,40,218,172,95,187,41,7,48, 49 | 255,246,132,175,196,207,194,237,144,153,95,88,203,59,116 50 | } 51 | -- 52 | ec.crypto_scalarmult_base(q, n1) 53 | --~ pt(q) 54 | assert(verify(q, qexp1)) 55 | -- 56 | ec.crypto_scalarmult_base(q, n2) 57 | --~ pt(q) 58 | assert(verify(q, qexp2)) 59 | -- 60 | ec.crypto_scalarmult_base(q, n3) 61 | --~ pt(q) 62 | assert(verify(q, qexp3)) 63 | end --test_scalarmult 64 | 65 | local function test_crypto_scalarmult() 66 | local n1 = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} 67 | local n2 = {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2} 68 | local q = {} 69 | local qexp = { 70 | 4,127,3,145,33,24,80,55,195,2,25,30,69,152,41,73,247, 71 | 217,178,195,16,207,14,23,51,33,83,95,188,20,223,62 72 | } 73 | ec.crypto_scalarmult(q, n1, n2) 74 | assert(verify(q, qexp)) 75 | end 76 | 77 | local function test_scalarmult() 78 | local sk = ('k'):rep(32) 79 | local pk = ec.scalarmult(sk, ec.base) 80 | assert( pk == xts( 81 | "8462fb3f0798f9fe2c39f3823bb41cd3effe70bb5c81735be46a143135c58454" 82 | )) 83 | end 84 | 85 | 86 | 87 | local function test_kexch() 88 | local ska = {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1} 89 | local skb = {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2} 90 | local pka, pkb = {}, {} 91 | ec.crypto_scalarmult_base(pka, ska) 92 | ec.crypto_scalarmult_base(pkb, skb) 93 | local r1, r2 = {}, {} 94 | ec.crypto_scalarmult(r1, ska, pkb) 95 | ec.crypto_scalarmult(r2, skb, pka) 96 | --~ pt(r1) 97 | --~ pt(r2) 98 | assert(verify(r1, r2)) 99 | end 100 | 101 | test_scalarmult() 102 | test_scalarmult_base() 103 | test_kexch() 104 | 105 | print("test_ec25519: ok") 106 | 107 | 108 | -------------------------------------------------------------------------------- /test/test_gimli.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2017 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------------------ 4 | -- gimli tests 5 | 6 | local bin = require "plc.bin" 7 | local xts = bin.hextos 8 | 9 | gim = require "plc.gimli" 10 | 11 | local function px12(st) 12 | local fmt = "%08x %08x %08x %08x \n" 13 | .. "%08x %08x %08x %08x \n" 14 | .. "%08x %08x %08x %08x \n" 15 | .. "-----------------------------------" 16 | print(string.format(fmt, 17 | st[1], st[2], st[3], st[4], 18 | st[5], st[6], st[7], st[8], 19 | st[9], st[10], st[11], st[12])) 20 | end 21 | 22 | -- core permutation test 23 | st = {} -- permutation state (12 32-byte integers) 24 | -- initialize the state 25 | -- see gimli-20170627/test.c in the reference implementation 26 | -- at https://gimli.cr.yp.to/ 27 | 28 | -- test the initial implementation 29 | for i = 0, 11 do 30 | st[i+1] = (i * i * i + i * 0x9e3779b9) & 0xffffffff 31 | end 32 | --~ px12(st) 33 | gim.gimli_core32(st) 34 | --~ px12(st) 35 | local expected = { 36 | 0xba11c85a, 0x91bad119, 0x380ce880, 0xd24c2c68, 37 | 0x3eceffea, 0x277a921c, 0x4f73a0bd, 0xda5a9cd8, 38 | 0x84b673f0, 0x34e52ff7, 0x9e2bef49, 0xf41bb8d6 39 | } 40 | for i = 1, 12 do assert(st[i] == expected[i]) end 41 | 42 | -- test the optimized implementation 43 | for i = 0, 11 do 44 | st[i+1] = (i * i * i + i * 0x9e3779b9) & 0xffffffff 45 | end 46 | --~ px12(st) 47 | gim.gimli_core32opt(st) 48 | --~ px12(st) 49 | local expected = { 50 | 0xba11c85a, 0x91bad119, 0x380ce880, 0xd24c2c68, 51 | 0x3eceffea, 0x277a921c, 0x4f73a0bd, 0xda5a9cd8, 52 | 0x84b673f0, 0x34e52ff7, 0x9e2bef49, 0xf41bb8d6 53 | } 54 | for i = 1, 12 do assert(st[i] == expected[i]) end 55 | 56 | 57 | print("test_gimli: ok") 58 | -------------------------------------------------------------------------------- /test/test_md5.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2017 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------------------ 4 | -- md5 tests 5 | 6 | local md5 = require "plc.md5" 7 | local bin = require "plc.bin" 8 | 9 | local xts = bin.hextos 10 | 11 | -- test vectors from RFC 1321 - https://www.ietf.org/rfc/rfc1321.txt 12 | 13 | local function test_md5() 14 | local hash = md5.hash 15 | assert(hash("") == xts("d41d8cd98f00b204e9800998ecf8427e")) 16 | assert(hash("a") == xts("0cc175b9c0f1b6a831c399e269772661")) 17 | assert(hash("abc") == xts("900150983cd24fb0d6963f7d28e17f72")) 18 | assert(hash("message digest") == xts("f96b697d7cb7938d525a2f31aaf161d0")) 19 | assert(hash("abcdefghijklmnopqrstuvwxyz") 20 | == xts("c3fcd3d76192e4007dfb496cca67e13b")) 21 | assert(hash( 22 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") 23 | == xts("d174ab98d277d9f5a5611c2c9f419d9f")) 24 | assert(hash("1234567890123456789012345678901234567890" 25 | .. "1234567890123456789012345678901234567890") 26 | == xts("57edf4a22be3c955ac49da2e2107b67a")) 27 | end 28 | 29 | test_md5() 30 | 31 | -------------------------------------------------------------------------------- /test/test_morus.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2018 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------------------ 4 | 5 | --[[ 6 | 7 | === test_morus (morus 1280, with 128 and 256-bit keys) 8 | 9 | test vectors from the Morus C reference implementation 10 | http://www3.ntu.edu.sg/home/wuhj/research/caesar/caesar.html 11 | 12 | 13 | ]] 14 | 15 | local bin = require "plc.bin" 16 | local stx, xts = bin.stohex, bin.hextos 17 | 18 | local mo = require "plc.morus" 19 | local k, iv, m, c, e, m2, err, tag, ad 20 | 21 | local function test() 22 | -- 16-byte key -- 1280_128 ----------------------------------------- 23 | k = xts'00000000000000000000000000000000' 24 | iv = xts'00000000000000000000000000000000' 25 | m = ""; ad = "" 26 | e = mo.encrypt(k, iv, m, ad) 27 | assert(#e == #ad + #m + 16) 28 | assert(e == xts"5bd2cba68ea7e72f6b3d0c155f39f962") 29 | m2, err = mo.decrypt(k, iv, e, #ad) 30 | assert(m2 == m) 31 | -- 32 | m = "\x01"; ad = "" 33 | e = mo.encrypt(k, iv, m, ad) 34 | assert(e == xts"ba ec1942a315a84695432a1255e6197878") 35 | m2, err = mo.decrypt(k, iv, e, #ad) 36 | assert(m2 == m) 37 | -- 38 | m = ""; ad = "\x01" 39 | e = mo.encrypt(k, iv, m, ad) 40 | --~ print(stx(e)) 41 | assert(e == xts"01 590caa148b848d7614315685377a0d42") --ad,tag 42 | m2, err = mo.decrypt(k, iv, e, #ad) 43 | assert(m2 == m) 44 | -- 45 | k = xts'01000000000000000000000000000000' 46 | m = "\x00"; ad = "\x00" 47 | e = mo.encrypt(k, iv, m, ad) 48 | assert(#e == #ad + #m + 16) 49 | assert(e == xts"00 cf f9f0a331e3de3293b9dd2e65ba820009")--ad,c,tag 50 | m2, err = mo.decrypt(k, iv, e, #ad) 51 | assert(m2 == m) 52 | -- 53 | k = xts'00000000000000000000000000000000' 54 | iv = xts'01000000000000000000000000000000' 55 | m = "\x00"; ad = "\x00" 56 | e = mo.encrypt(k, iv, m, ad) 57 | assert(#e == #ad + #m + 16) 58 | assert(e == xts"00 09 c957f9ca617876b5205155cd936eb9bb")--ad,c,tag 59 | m2, err = mo.decrypt(k, iv, e, #ad) 60 | assert(m2 == m) 61 | -- 62 | k = xts'000102030405060708090a0b0c0d0e0f' 63 | iv = xts'000306090c0f1215181b1e2124272a2d' 64 | m = xts'01010101010101010101010101010101' 65 | ad = xts'01010101010101010101010101010101' 66 | e = mo.encrypt(k, iv, m, ad) 67 | --~ print(stx(e)) 68 | assert(#e == #ad + #m + 16) 69 | assert(e == xts[[ 70 | 01010101010101010101010101010101 71 | b64ee39fc045475e97b41bd08277b4cb 72 | e989740eb075f75bd57a43a250f53765 73 | ]])--ad,c,tag 74 | m2, err = mo.decrypt(k, iv, e, #ad) 75 | assert(m2 == m) 76 | -- 77 | k = xts'000102030405060708090a0b0c0d0e0f' 78 | iv = xts'000306090c0f1215181b1e2124272a2d' 79 | m = xts[[ 80 | 00070e151c232a31383f464d545b626970777e858c939aa1a8afb6bdc4cbd2d9 81 | e0e7eef5fc030a11181f262d343b424950575e656c737a81888f969da4abb2b9 82 | c0c7ced5dce3eaf1f8 ]] 83 | ad = xts[[ 84 | 00050a0f14191e23282d32373c41464b50555a5f64696e73787d82878c91969b 85 | a0a5aaafb4b9be ]] 86 | e = mo.encrypt(k, iv, m, ad) 87 | --~ print(stx(e)) 88 | assert(#e == #ad + #m + 16) 89 | assert(e == ad .. xts[[ 90 | 0861b4924850e8a945e60ec08a1b04f3c77dd2b05ccb05c05c567be8cdfd4582 91 | 28a390c4117b66d71fade7f89902e4d500389a275cb0ce5685f3a21beb6d6519 92 | f465b96f1eaf9eeea2 5e43f30fa0adb318083a795fc23df52c ]]) 93 | m2, err = mo.decrypt(k, iv, e, #ad) 94 | assert(m2 == m) 95 | -- 96 | -- 32-byte key -- 1280_256 ----------------------------------------- 97 | -- 98 | k = xts'000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f' 99 | iv = xts'000306090c0f1215181b1e2124272a2d' 100 | m = xts[[ 01010101010101010101010101010101 ]] 101 | ad = xts[[ 01010101010101010101010101010101 ]] 102 | e = mo.encrypt(k, iv, m, ad) 103 | assert(#e == #ad + #m + 16) 104 | assert(e == ad .. xts[[ 105 | aecb6f5991a11746831740e4d45b6c26 c3107488470f05e6828472ac0264045d ]]) 106 | m2, err = mo.decrypt(k, iv, e, #ad) 107 | assert(m2 == m) 108 | -- 109 | k = xts'000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f' 110 | iv = xts'000306090c0f1215181b1e2124272a2d' 111 | m = xts[[ 112 | 00070e151c232a31383f464d545b626970777e858c939aa1a8afb6bdc4cbd2d9 113 | e0e7eef5fc030a11181f262d343b424950575e656c737a81888f969da4abb2b9 114 | c0c7ced5dce3eaf1f8 ]] 115 | ad = xts[[ 116 | 00050a0f14191e23282d32373c41464b50555a5f64696e73787d82878c91969b 117 | a0a5aaafb4b9be ]] 118 | e = mo.encrypt(k, iv, m, ad) 119 | assert(#e == #ad + #m + 16) 120 | assert(e == ad .. xts[[ 121 | 3e440c73993c55074d4749d6cd8ceddebb95ea8d2387062237349123c75959bf 122 | a3ff44b18395a0bfc834d5f2de24845bffdba576afab00e798ad5a1666892883 123 | 73f84ead85eb77aa2d f3166bbf6f94a1932b4b2471e8437206 ]]) 124 | m2, err = mo.decrypt(k, iv, e, #ad) 125 | assert(m2 == m) 126 | -- 127 | end 128 | 129 | test() 130 | 131 | print("test_morus: ok") 132 | -------------------------------------------------------------------------------- /test/test_norx.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2017 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------------------ 4 | -- norx tests 5 | 6 | local norx = require "plc.norx" 7 | 8 | local char = string.char 9 | local concat = table.concat 10 | 11 | local bin = require "plc.bin" -- for hex conversion 12 | local hextos = bin.hextos 13 | 14 | 15 | ------------------------------------------------------------------------ 16 | 17 | local function test_norx() 18 | -- test vector from the specification at 19 | -- https://github.com/norx/resources/blob/master/specs/norxv30.pdf 20 | -- (norx here is the default NORX 64-4-1 variant) 21 | local t 22 | -- key: 00 01 02 ... 1F (32 bytes) 23 | t = {}; for i = 1, 32 do t[i] = char(i-1) end; 24 | local key = concat(t) 25 | -- nonce: 30 31 32 ... 4F (32 bytes) 26 | t = {}; for i = 1, 32 do t[i] = char(i+32-1) end; 27 | local nonce = concat(t) 28 | -- plain text, header, trailer: 00 01 02 ... 7F (128 bytes) 29 | t = {}; for i = 1, 128 do t[i] = char(i-1) end; 30 | local plain = concat(t) 31 | local header, trailer = plain, plain 32 | local crypted = norx.aead_encrypt(key, nonce, plain, header, trailer) 33 | local authtag = crypted:sub(#crypted-32+1) -- 32 last bytes 34 | assert(authtag == hextos [[ 35 | D1 F2 FA 33 05 A3 23 76 E2 3A 61 D1 C9 89 30 3F 36 | BF BD 93 5A A5 5B 17 E4 E7 25 47 33 C4 73 40 8E 37 | ]]) 38 | local p = assert(norx.aead_decrypt(key, nonce, crypted, header, trailer)) 39 | --print'---plain'; print(stohex(p, 16)) 40 | assert(#crypted == #plain + 32) 41 | assert(p == plain) 42 | -- 43 | -- test proper block handling and padding for various input sizes 44 | for i = 1, 66 do 45 | plain = ('p'):rep(i) 46 | crypted = norx.aead_encrypt(key, nonce, plain) 47 | assert(#crypted == #plain + 32) 48 | assert(norx.aead_decrypt(key, nonce, crypted) == plain) 49 | end 50 | -- 51 | -- test against AD handling issues 52 | plain = ('p'):rep(65) 53 | for i = 1, 9 do 54 | a = ('A'):rep(i) -- AD prefix 55 | for j = 1, 9 do 56 | z = ('Z'):rep(i) -- AD suffix 57 | crypted = norx.aead_encrypt(key, nonce, plain, a, z) 58 | assert(#crypted == #plain + 32) 59 | assert(norx.aead_decrypt(key, nonce, crypted, a, z) == plain) 60 | end 61 | end 62 | -- 63 | end --test_norx() 64 | 65 | 66 | test_norx() 67 | 68 | print("test_norx: ok") 69 | -------------------------------------------------------------------------------- /test/test_norx32.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2017 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------------------ 4 | -- norx tests 5 | 6 | local norx = require "plc.norx32" 7 | 8 | local char = string.char 9 | local concat = table.concat 10 | 11 | local bin = require "plc.bin" -- for hex conversion 12 | local hextos = bin.hextos 13 | 14 | 15 | ------------------------------------------------------------------------ 16 | 17 | local function test_norx32() 18 | -- test vector from the specification at 19 | -- https://github.com/norx/resources/blob/master/specs/norxv30.pdf 20 | -- (norx here is the NORX 32-4-1 variant) 21 | local t 22 | -- key: 00 01 02 ... 0F (16 bytes) 23 | t = {}; for i = 1, 16 do t[i] = char(i-1) end; 24 | local key = concat(t) 25 | -- nonce: 20 21 22 ... 2F (16 bytes) 26 | t = {}; for i = 1, 16 do t[i] = char(i+32-1) end; 27 | local nonce = concat(t) 28 | -- plain text, header, trailer: 00 01 02 ... 7F (128 bytes) 29 | t = {}; for i = 1, 128 do t[i] = char(i-1) end; 30 | local plain = concat(t) 31 | local header, trailer = plain, plain 32 | local crypted = norx.aead_encrypt(key, nonce, plain, header, trailer) 33 | -- print'---encrypted'; print(stohex(crypted, 16)) 34 | local authtag = crypted:sub(#crypted-16+1) 35 | assert(authtag == hextos"D5 54 E4 BC 6B 5B B7 89 54 77 59 EA CD FF CF 47") 36 | local p = assert(norx.aead_decrypt(key, nonce, crypted, header, trailer)) 37 | --print'---plain'; print(stohex(p, 16)) 38 | assert(#crypted == #plain + 16) 39 | assert(p == plain) 40 | -- 41 | -- test proper block handling and padding for various input sizes 42 | for i = 1, 66 do 43 | plain = ('p'):rep(i) 44 | crypted = norx.aead_encrypt(key, nonce, plain) 45 | assert(#crypted == #plain + 16) 46 | assert(norx.aead_decrypt(key, nonce, crypted) == plain) 47 | end 48 | -- 49 | -- test against AD handling issues 50 | plain = ('p'):rep(65) 51 | for i = 1, 9 do 52 | a = ('A'):rep(i) -- AD prefix 53 | for j = 1, 9 do 54 | z = ('Z'):rep(i) -- AD suffix 55 | crypted = norx.aead_encrypt(key, nonce, plain, a, z) 56 | assert(#crypted == #plain + 16) 57 | assert(norx.aead_decrypt(key, nonce, crypted, a, z) == plain) 58 | end 59 | end 60 | -- 61 | end --test_norx32() 62 | 63 | 64 | test_norx32() 65 | 66 | print("test_norx32: ok") 67 | -------------------------------------------------------------------------------- /test/test_poly1305.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2015 Phil Leblanc -- see LICENSE file 2 | ------------------------------------------------------------ 3 | 4 | local bin = require "plc.bin" -- used for hex conversions 5 | 6 | 7 | -- test poly1305.lua 8 | 9 | local poly = require "plc.poly1305" 10 | 11 | local test_poly_auth = function () 12 | -- "nacl" test vector 13 | local naclkey = string.char( 14 | 0xee,0xa6,0xa7,0x25,0x1c,0x1e,0x72,0x91, 15 | 0x6d,0x11,0xc2,0xcb,0x21,0x4d,0x3c,0x25, 16 | 0x25,0x39,0x12,0x1d,0x8e,0x23,0x4e,0x65, 17 | 0x2d,0x65,0x1f,0xa4,0xc8,0xcf,0xf8,0x80 18 | ) 19 | local naclmsg = string.char( 20 | 0x8e,0x99,0x3b,0x9f,0x48,0x68,0x12,0x73, 21 | 0xc2,0x96,0x50,0xba,0x32,0xfc,0x76,0xce, 22 | 0x48,0x33,0x2e,0xa7,0x16,0x4d,0x96,0xa4, 23 | 0x47,0x6f,0xb8,0xc5,0x31,0xa1,0x18,0x6a, 24 | 0xc0,0xdf,0xc1,0x7c,0x98,0xdc,0xe8,0x7b, 25 | 0x4d,0xa7,0xf0,0x11,0xec,0x48,0xc9,0x72, 26 | 0x71,0xd2,0xc2,0x0f,0x9b,0x92,0x8f,0xe2, 27 | 0x27,0x0d,0x6f,0xb8,0x63,0xd5,0x17,0x38, 28 | 0xb4,0x8e,0xee,0xe3,0x14,0xa7,0xcc,0x8a, 29 | 0xb9,0x32,0x16,0x45,0x48,0xe5,0x26,0xae, 30 | 0x90,0x22,0x43,0x68,0x51,0x7a,0xcf,0xea, 31 | 0xbd,0x6b,0xb3,0x73,0x2b,0xc0,0xe9,0xda, 32 | 0x99,0x83,0x2b,0x61,0xca,0x01,0xb6,0xde, 33 | 0x56,0x24,0x4a,0x9e,0x88,0xd5,0xf9,0xb3, 34 | 0x79,0x73,0xf6,0x22,0xa4,0x3d,0x14,0xa6, 35 | 0x59,0x9b,0x1f,0x65,0x4c,0xb4,0x5a,0x74, 36 | 0xe3,0x55,0xa5 37 | ) 38 | local naclmac = string.char( 39 | 0xf3,0xff,0xc7,0x70,0x3f,0x94,0x00,0xe5, 40 | 0x2a,0x7d,0xfb,0x4b,0x3d,0x33,0x05,0xd9 41 | ) 42 | local mac = poly.auth(naclmsg, naclkey) 43 | assert (mac == naclmac) 44 | 45 | -- "wrap" test vector 46 | -- generates a final value of (2^130 - 2) == 3 47 | local wrapkey = string.char( 48 | 0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 49 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 50 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 51 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 52 | ) 53 | local wrapmsg = string.char( 54 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 55 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff 56 | ) 57 | local wrapmac = string.char( 58 | 0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 59 | 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 60 | ) 61 | mac = poly.auth(wrapmsg, wrapkey) 62 | assert (mac == wrapmac) 63 | 64 | -- rfc 7539 test vector 65 | local rfcmsg = "Cryptographic Forum Research Group" 66 | local rfckey = bin.hextos( 67 | "85d6be7857556d337f4452fe42d506a80103808afb0db2fd4abff6af4149f51b") 68 | local rfcmac = bin.hextos("a8061dc1305136c6c22b8baf0c0127a9") 69 | mac = poly.auth(rfcmsg, rfckey) 70 | assert (mac == rfcmac) 71 | end 72 | 73 | test_poly_auth() 74 | 75 | print("test_poly1305: ok") 76 | -------------------------------------------------------------------------------- /test/test_rabbit.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2015 Phil Leblanc -- see LICENSE file 2 | ------------------------------------------------------------ 3 | 4 | -- test rabbit.lua 5 | 6 | 7 | local rabbit = require "plc.rabbit" 8 | 9 | local bin = require "plc.bin" 10 | local xts = bin.hextos 11 | 12 | ------------------------------------------------------------ 13 | 14 | local function test_rabbit_encrypt() 15 | -- quick test with some eSTREAM test vectors 16 | local key, iv, exp, ec 17 | local key0 = ('\0'):rep(16) 18 | local iv0 = ('\0'):rep(8) 19 | local txt0 = ('\0'):rep(48) 20 | ec = rabbit.encrypt(key0, iv0, txt0) 21 | exp = xts[[ EDB70567375DCD7CD89554F85E27A7C6 22 | 8D4ADC7032298F7BD4EFF504ACA6295F 23 | 668FBF478ADB2BE51E6CDE292B82DE2A ]] 24 | assert(ec == exp) 25 | 26 | iv = '\x27\x17\xF4\xD2\x1A\x56\xEB\xA6' 27 | ec = rabbit.encrypt(key0, iv, txt0) 28 | exp = xts[[ 4D1051A123AFB670BF8D8505C8D85A44 29 | 035BC3ACC667AEAE5B2CF44779F2C896 30 | CB5115F034F03D31171CA75F89FCCB9F ]] 31 | assert(ec == exp) 32 | 33 | --Set 5, vector# 63 34 | iv = xts "0000000000000001" 35 | ec = rabbit.encrypt(key0, iv, txt0) 36 | exp = xts[[ 55FB0B90A9FB953AE96D372BADBEBD30 37 | F531A454D31B669BCD8BAAD78C6C9994 38 | FFCCEC7ACB22F914A072DA22A617C0B7 ]] 39 | assert(ec == exp) 40 | 41 | --Set6, vector# 0 42 | key = xts "0053A6F94C9FF24598EB3E91E4378ADD" 43 | iv = xts "0D74DB42A91077DE" 44 | ec = rabbit.encrypt(key, iv, txt0) 45 | exp = xts[[ 75D186D6BC6905C64F1B2DFDD51F7BFC 46 | D74F926E6976CD0A9B1A3AE9DD8CB43F 47 | F5CD60F2541FF7F22C5C70CE07613989 ]] 48 | assert(ec == exp) 49 | 50 | -- test proper block handling and padding for various input sizes 51 | for i = 1, 66 do 52 | plain = ('p'):rep(i) 53 | crypted = rabbit.encrypt(key, iv, plain) 54 | assert(#crypted == #plain) 55 | assert(rabbit.decrypt(key, iv, crypted) == plain) 56 | end 57 | -- 58 | 59 | return true 60 | end 61 | 62 | test_rabbit_encrypt() 63 | 64 | print("test_rabbit: ok") 65 | -------------------------------------------------------------------------------- /test/test_rc4.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2015 Phil Leblanc -- see LICENSE file 2 | 3 | ------------------------------------------------------------ 4 | -- base64 tests 5 | 6 | local rc4 = require "plc.rc4" 7 | 8 | local function test_rc4() 9 | local k = ('1'):rep(16) 10 | local plain = 'abcdef' 11 | local encr = rc4.encrypt(k, plain) 12 | assert(encr == "\x25\x98\xfa\xe1\x4d\x66") 13 | encr = rc4.encrypt(k, plain, 0) -- "raw", no drop 14 | assert(encr == "\x01\x78\xa1\x09\xf2\x21") 15 | plain = plain:rep(100) 16 | assert(plain == rc4.encrypt(k, rc4.decrypt(k, plain))) 17 | 18 | -- test proper block handling and padding for various input sizes 19 | for i = 1, 66 do 20 | plain = ('p'):rep(i) 21 | crypted = rc4.encrypt(k, plain) 22 | assert(#crypted == #plain) 23 | assert(rc4.decrypt(k, crypted) == plain) 24 | end 25 | -- 26 | 27 | end 28 | 29 | 30 | test_rc4() 31 | 32 | print("test_rc4: ok") 33 | -------------------------------------------------------------------------------- /test/test_salsa20.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2017 Pierre Chapuis -- see LICENSE file 2 | ------------------------------------------------------------ 3 | 4 | -- test salsa20.lua 5 | 6 | local salsa20 = require "plc.salsa20" 7 | local bin = require "plc.bin" 8 | 9 | local function test_salsa20_encrypt_decrypt() 10 | -- Check we can decrypt what we encrypted. 11 | local plain = 12 | "Ladies and Gentlemen of the class of '99: If I could " 13 | .. "offer you only one tip for the future, sunscreen would be it." 14 | local key = 15 | "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" 16 | .. "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" 17 | local nonce = "\x00\x00\x00\x4a\x00\x02\x00\x00" 18 | assert(#key == salsa20.key_size) 19 | assert(#nonce == salsa20.nonce_size) 20 | local counter = 1 21 | local et = salsa20.encrypt(key, counter, nonce, plain) 22 | assert(plain == salsa20.encrypt(key, counter, nonce, et)) 23 | -- 24 | -- test some input lengths 25 | for i = 1, 66 do 26 | plain = ('p'):rep(i) 27 | et = salsa20.encrypt(key, counter, nonce, plain) 28 | local dt = salsa20.decrypt(key, counter, nonce, et) 29 | assert((#et == #plain) and (dt == plain)) 30 | end 31 | -- 32 | return true 33 | end 34 | 35 | local function _test_salsa20_ecrypt(hex_key, hex_nonce, bytes, hex_expected) 36 | local key = bin.hextos(hex_key) 37 | local nonce = bin.hextos(hex_nonce) 38 | local expected = bin.hextos(hex_expected) 39 | assert(#key == salsa20.key_size) 40 | assert(#nonce == salsa20.nonce_size) 41 | assert(bytes % 64 == 0) 42 | local plain = string.rep("\0", bytes) 43 | local et = salsa20.encrypt(key, 0, nonce, plain) 44 | local digest = string.rep("\0", 64) 45 | for i = 0, bytes - 1, 64 do 46 | digest = bin.xor8(digest, et:sub(i+1, i+64)) 47 | end 48 | assert(digest == expected) 49 | end 50 | 51 | local function test_salsa20_ecrypt_1_0() 52 | -- Test vector 0 from ECRYPT set 1. 53 | -- http://www.ecrypt.eu.org/stream/svn/viewcvs.cgi/ecrypt/trunk/submissions/salsa20/full/verified.test-vectors 54 | local key = [[ 55 | 80000000000000000000000000000000 56 | 00000000000000000000000000000000 57 | ]] 58 | local nonce = "0000000000000000" 59 | local bytes = 512 60 | local expected = [[ 61 | 50EC2485637DB19C6E795E9C73938280 62 | 6F6DB320FE3D0444D56707D7B456457F 63 | 3DB3E8D7065AF375A225A70951C8AB74 64 | 4EC4D595E85225F08E2BC03FE1C42567 65 | ]] 66 | _test_salsa20_ecrypt(key, nonce, bytes, expected) 67 | end 68 | 69 | 70 | local function test_salsa20_ecrypt_6_0() 71 | -- Test vector 0 from ECRYPT set 6. 72 | -- http://www.ecrypt.eu.org/stream/svn/viewcvs.cgi/ecrypt/trunk/submissions/salsa20/full/verified.test-vectors 73 | local key = [[ 74 | 0053A6F94C9FF24598EB3E91E4378ADD 75 | 3083D6297CCF2275C81B6EC11467BA0D 76 | ]] 77 | local nonce = "0D74DB42A91077DE" 78 | local bytes = 131072 79 | local expected = [[ 80 | C349B6A51A3EC9B712EAED3F90D8BCEE 81 | 69B7628645F251A996F55260C62EF31F 82 | D6C6B0AEA94E136C9D984AD2DF3578F7 83 | 8E457527B03A0450580DD874F63B1AB9 84 | ]] 85 | _test_salsa20_ecrypt(key, nonce, bytes, expected) 86 | end 87 | 88 | local function test_hsalsa20() 89 | -- libsodium core2 test 90 | local key = bin.hextos [[ 91 | 1B27556473E985D462CD51197A9A46C7 92 | 6009549EAC6474F206C4EE0844F68389 93 | ]] 94 | local nonce = bin.hextos("69696EE955B62B73") 95 | local counter = string.unpack("I8', p) 30 | e = xts"497df3d072612cb5"; eu = sunpack('>I8', e) 31 | assert(xtea.encrypt_s8(st1, p) == e) 32 | assert(xtea.encrypt_u64(st1, pu) == eu) 33 | assert(xtea.decrypt_s8(st1, e) == p) 34 | assert(xtea.decrypt_u64(st1, eu) == pu) 35 | -- 36 | p = xts"4142434445464748"; pu = sunpack('>I8', p) 37 | e = xts"a0390589f8b8efa5"; eu = sunpack('>I8', e) 38 | assert(xtea.encrypt_s8(st0, p) == e) 39 | assert(xtea.encrypt_u64(st0, pu) == eu) 40 | assert(xtea.decrypt_s8(st0, e) == p) 41 | assert(xtea.decrypt_u64(st0, eu) == pu) 42 | -- 43 | -- test stream encryption 44 | iv = "12345678" 45 | local function test_ctr(p) 46 | e = xtea.encrypt(k1, iv, p) 47 | --px(e) 48 | assert(#e == #p) 49 | assert(xtea.decrypt(k1, iv, e) == p) 50 | end 51 | test_ctr"" 52 | test_ctr"a" 53 | test_ctr(("a"):rep(7)) 54 | test_ctr(("a"):rep(8)) 55 | test_ctr(("a"):rep(9)) 56 | test_ctr(("a"):rep(15)) 57 | test_ctr(("a"):rep(100)) 58 | 59 | return true 60 | end 61 | 62 | test_xtea() 63 | 64 | print("test_xtea: ok") 65 | -------------------------------------------------------------------------------- /test_all.lua: -------------------------------------------------------------------------------- 1 | require "test.test_bin" 2 | require "test.test_base64" 3 | require "test.test_base58" 4 | require "test.test_base85" 5 | 6 | -- checksums, crypto hashes 7 | require "test.test_checksum" 8 | require "test.test_md5" 9 | require "test.test_sha2" 10 | require "test.test_sha3" 11 | require "test.test_blake2b" 12 | require "test.test_siphash" 13 | 14 | --encryption 15 | require "test.test_rc4" 16 | require "test.test_xtea" 17 | require "test.test_rabbit" 18 | require "test.test_salsa20" 19 | require "test.test_chacha20" 20 | require "test.test_poly1305" 21 | require "test.test_aead_chacha_poly" 22 | require "test.test_norx" 23 | require "test.test_norx32" 24 | require "test.test_morus" 25 | 26 | -- gimli-based functions 27 | require "test.test_gimli" 28 | 29 | -- pk / EC scalar mult for DH key exchange 30 | require "test.test_ec25519" 31 | 32 | -- NaCl box(), secret_box() 33 | require "test.test_box" 34 | 35 | -------------------------------------------------------------------------------- /test_perf.lua: -------------------------------------------------------------------------------- 1 | -- Copyright (c) 2018 Phil Leblanc -- see LICENSE file 2 | ------------------------------------------------------------------------ 3 | 4 | --[[ 5 | 6 | === plc - crude performance tests 7 | 8 | 9 | ]] 10 | 11 | local bin = require "plc.bin" 12 | local stx = bin.stohex 13 | local xts = bin.hextos 14 | local function px(s) print(stx(s, 16, " ")) end 15 | local function pf(...) print(string.format(...)) end 16 | 17 | local function pst(st) 18 | for i = 1,8 do 19 | pf("x[%d]: %08X c[%d]: %08X", 20 | i, st.x[i], i, st.c[i] ) 21 | end 22 | end 23 | 24 | local spack, sunpack = string.pack, string.unpack 25 | local app, concat = table.insert, table.concat 26 | local char, byte, strf = string.char, string.byte, string.format 27 | 28 | ------------------------------------------------------------------------ 29 | local rc4 = require "plc.rc4" 30 | local rabbit = require "plc.rabbit" 31 | local cha = require "plc.chacha20" 32 | local salsa = require "plc.salsa20" 33 | local sha2 = require "plc.sha2" 34 | local sha3 = require "plc.sha3" 35 | local poly = require "plc.poly1305" 36 | local chk = require "plc.checksum" 37 | local xtea = require "plc.xtea" 38 | local blake2b = require "plc.blake2b" 39 | local norx = require "plc.norx" 40 | local norx32 = require "plc.norx32" 41 | local md5 = require "plc.md5" 42 | local morus = require "plc.morus" 43 | local ec25519 = require "plc.ec25519" 44 | 45 | local base64 = require "plc.base64" 46 | local base58 = require "plc.base58" 47 | local base85 = require "plc.base85" 48 | 49 | ------------------------------------------------------------ 50 | 51 | local t0, c0, t1, c1, desc, cmt, sizemb 52 | 53 | local function start(d, c) 54 | desc = d 55 | cmt = c and "-- " .. c or "" --optional comment 56 | t0, c0 = os.time(), os.clock() 57 | end 58 | 59 | local function done() 60 | local dt = os.time()-t0 61 | local dc = os.clock()-c0 62 | pf("- %-20s %7.1f %s", desc, dc, cmt) 63 | end 64 | 65 | local sizemb = 10 -- plain text size (in MBytes) 66 | local mega = 1024 * 1024 67 | local size = mega * sizemb 68 | local plain = ('a'):rep(size) 69 | local k32 = ('k'):rep(32) 70 | local k16 = ('k'):rep(16) 71 | local iv8 = ('i'):rep(8) 72 | 73 | ------------------------------------------------------------ 74 | 75 | local function perf_md5() 76 | local m = plain 77 | 78 | for j = 1, 1 do 79 | start("md5") 80 | local dig = md5.hash(m) 81 | done() 82 | end 83 | -- 84 | end --perf_md5 85 | 86 | ------------------------------------------------------------ 87 | 88 | local function perf_sha2() 89 | local et, h -- encrypted text, hash/hmac 90 | 91 | start("sha2-256") 92 | h = sha2.sha256(plain) 93 | done() 94 | 95 | start("sha2-512") 96 | h = sha2.sha512(plain) 97 | done() 98 | 99 | end --perf_sha2 100 | 101 | ------------------------------------------------------------ 102 | 103 | local function perf_sha3() 104 | local et, h -- encrypted text, hash/hmac 105 | 106 | start("sha3-256") 107 | h = sha3.sha256(plain) 108 | done() 109 | 110 | start("sha3-512") 111 | h = sha3.sha512(plain) 112 | done() 113 | end --perf_sha3 114 | 115 | 116 | ------------------------------------------------------------ 117 | 118 | local function perf_blake2b() 119 | local dig 120 | -- 121 | for j = 1, 1 do 122 | start("blake2b-512") 123 | dig = blake2b.hash(plain) 124 | done() 125 | end 126 | -- 127 | for j = 1, 1 do 128 | start("blake2b-256") 129 | dig = blake2b.hash(plain, 32) -- 32-byte digest 130 | done() 131 | end 132 | -- 133 | end --perf_blake2b 134 | 135 | 136 | 137 | 138 | ------------------------------------------------------------ 139 | 140 | local function perf_encrypt() 141 | local nonce = ('n'):rep(12) 142 | local nonce8 = ('n'):rep(8) 143 | local counter = 1 144 | local aad = "\x50\x51\x52\x53\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" 145 | local iv = "\x40\x41\x42\x43\x44\x45\x46\x47" 146 | local const = "\x07\x00\x00\x00" 147 | local et, h -- encrypted text, hash/hmac 148 | 149 | start("rc4 raw") 150 | et = rc4.rc4raw(k16, plain) 151 | done() 152 | 153 | start("rabbit") 154 | et = rabbit.encrypt(k16, iv8, plain) 155 | done() 156 | -- 157 | end --perf_encrypt 158 | 159 | ------------------------------------------------------------ 160 | 161 | local function perf_xtea() 162 | local sub = string.sub 163 | local et 164 | -- 165 | start("xtea ctr") 166 | et = xtea.encrypt(k16, iv8, plain) 167 | done() 168 | -- 169 | start("xtea", "encrypt block only") 170 | local st = xtea.keysetup(k16) 171 | for i = 1, #plain//8 do 172 | xtea.encrypt_u64(st, 0xaaaa5555aaaa5555) 173 | end 174 | done() 175 | -- 176 | end --perf_xtea 177 | 178 | ------------------------------------------------------------ 179 | 180 | local function perf_encrypt20() 181 | local nonce = ('n'):rep(12) 182 | local nonce8 = ('n'):rep(8) 183 | local counter = 1 184 | local aad = "\x50\x51\x52\x53\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7" 185 | local iv = "\x40\x41\x42\x43\x44\x45\x46\x47" 186 | local const = "\x07\x00\x00\x00" 187 | local et, h -- encrypted text, hash/hmac 188 | 189 | start("chacha20") 190 | et = cha.encrypt(k32, counter, nonce, plain) 191 | done() 192 | 193 | start("salsa20") 194 | et = salsa.encrypt(k32, counter, nonce8, plain) 195 | done() 196 | -- 197 | end --perf_encrypt20 198 | 199 | ------------------------------------------------------------ 200 | 201 | local function perf_norx() 202 | local k = ('k'):rep(32) -- key 203 | local n = ('n'):rep(32) -- nonce 204 | local a = ('a'):rep(16) -- header ad (61 61 ...) 205 | local z = ('z'):rep(8) -- trailer ad (7a 7a ...) 206 | local m = plain 207 | -- 208 | for i = 1, 1 do 209 | start("norx encrypt") 210 | local c = norx.aead_encrypt(k, n, plain, a, z) 211 | done() 212 | start("norx decrypt") 213 | local p = norx.aead_decrypt(k, n, c, a, z) 214 | assert(p == plain) 215 | done() 216 | end 217 | -- 218 | for j = 1, 1 do 219 | local pt = {} 220 | local cnt = 256 * 10 -- cnt * 4k = 10mb 221 | for i = 1, cnt do pt[i] = (char(i%256)):rep(4096) end 222 | local ct = {} 223 | start("norx encrypt", "10mb, 4k messages") 224 | for i = 1, cnt do 225 | ct[i] = norx.aead_encrypt(k, n, pt[i]) 226 | end 227 | done() 228 | start("norx decrypt", "10mb, 4k messages") 229 | for i = 1, cnt do 230 | local p = norx.aead_decrypt(k, n, ct[i]) 231 | end 232 | done() 233 | end 234 | -- 235 | end --perf_norx 236 | 237 | ------------------------------------------------------------ 238 | 239 | local function perf_norx32() 240 | local k = ('k'):rep(16) -- key 241 | local n = ('n'):rep(16) -- nonce 242 | local a = ('a'):rep(16) -- header ad (61 61 ...) 243 | local z = ('z'):rep(8) -- trailer ad (7a 7a ...) 244 | local m = plain 245 | 246 | for j = 1, 1 do 247 | start("norx32 encrypt") 248 | local c = norx32.aead_encrypt(k, n, plain, a, z) 249 | done() 250 | start("norx32 decrypt") 251 | local p = norx32.aead_decrypt(k, n, c, a, z) 252 | assert(p == plain) 253 | done() 254 | end 255 | -- 256 | end --perf_norx32 257 | 258 | ------------------------------------------------------------ 259 | 260 | local function perf_morus() 261 | local k = ('k'):rep(16) -- key 262 | local n = ('n'):rep(16) -- nonce 263 | local a = ('a'):rep(16) -- ad (61 61 ...) 264 | local sizemb = 100 265 | local m = ('m'):rep(sizemb * 1024 * 1024) 266 | 267 | for j = 1, 1 do 268 | start("morus encrypt", "100mb") 269 | local c = morus.encrypt(k, n, m) 270 | done() 271 | start("morus decrypt", "100mb") 272 | local p = morus.decrypt(k, n, c) 273 | assert(p == m) 274 | done() 275 | start("morus-based xof", "100mb") 276 | local c = morus.x_hash(m) 277 | done() 278 | 279 | end 280 | -- 281 | end --perf_morus 282 | 283 | ------------------------------------------------------------ 284 | 285 | local function perf_ec25519() 286 | local base = ec25519.base 287 | -- 288 | start("ec25519 scalarmult", "100 times") 289 | for i = 1, 100 do et = ec25519.scalarmult(k32, base) end 290 | done() 291 | -- 292 | end --perf_ec25519 293 | 294 | ------------------------------------------------------------ 295 | 296 | local function perf_xor() 297 | -- 298 | start("xor1, k16") 299 | et = bin.xor1(k16, plain) 300 | done() 301 | -- 302 | -- xor64 removed 303 | -- 304 | start("xor8, k16") 305 | et = bin.xor8(k16, plain) 306 | done() 307 | -- 308 | end --perf_xor 309 | 310 | ------------------------------------------------------------ 311 | 312 | local function perf_base() 313 | local et -- expected text 314 | local s64 = ("a"):rep(64) -- for base58 test 315 | 316 | start("base64 encode", "result: 13.3mb") 317 | et = base64.encode(plain) 318 | done() 319 | 320 | start("base64 decode", "result: 7.5mb") 321 | et = base64.decode(plain) 322 | done() 323 | 324 | start("base85 encode", "result: 12.5mb") 325 | et = base85.encode(plain) 326 | done() 327 | 328 | start("base85 decode", "result: 8mb") 329 | et = base85.decode(plain) 330 | done() 331 | 332 | start("base58 encode", "64 bytes x 10,000") 333 | for i = 1, 10000 do et = base58.encode(s64) end 334 | done() 335 | 336 | start("base58 decode", "64 bytes x 10,000") 337 | for i = 1, 10000 do et = base58.decode(s64) end 338 | done() 339 | 340 | 341 | end --perf_base 342 | 343 | ------------------------------------------------------------ 344 | 345 | local function perf_misc() 346 | local et, h -- encrypted text, hash/hmac 347 | 348 | start("adler-32") 349 | h = chk.adler32(plain) 350 | done() 351 | 352 | start("crc-32") 353 | h = chk.crc32(plain) 354 | done() 355 | 356 | start("crc-32 (no table)") 357 | h = chk.crc32_nt(plain) 358 | done() 359 | 360 | start("poly1305 hmac") 361 | h = poly.auth(plain, k32) 362 | done() 363 | 364 | end --perf_misc 365 | 366 | 367 | 368 | ------------------------------------------------------------ 369 | 370 | print(_VERSION) 371 | 372 | print("Plain text: 10 MBytes except where noted") 373 | print("Elapsed times in seconds") 374 | 375 | print("\n-- hash \n") 376 | 377 | perf_md5() 378 | perf_sha2() 379 | perf_sha3() 380 | perf_blake2b() 381 | 382 | print("\n-- encryption \n") 383 | 384 | perf_encrypt() 385 | perf_xtea() 386 | perf_encrypt20() 387 | perf_norx() 388 | perf_norx32() 389 | perf_morus() 390 | 391 | print("\n-- elliptic curve \n") 392 | 393 | perf_ec25519() 394 | 395 | print("\n-- base encoding \n") 396 | 397 | perf_base() 398 | 399 | print("\n-- misc \n") 400 | 401 | perf_misc() 402 | perf_xor() 403 | 404 | --[[ 405 | 406 | tests run on an average/old laptop 407 | (Linux 4.4 x86_64CPU Intel i5 M430 @2.27GHz) 408 | with Lua 5.3.4 409 | 410 | Plain text: 10 MBytes except where noted 411 | Elapsed times in seconds 412 | 413 | -- hash 414 | 415 | - md5 3.7 416 | - sha2-256 9.1 417 | - sha2-512 6.4 418 | - sha3-256 23.2 419 | - sha3-512 43.0 420 | - blake2b-512 9.4 421 | - blake2b-256 9.3 422 | 423 | -- encryption 424 | 425 | - rc4 raw 7.4 426 | - rabbit 4.7 427 | - xtea ctr 11.0 428 | - xtea 8.9 -- encrypt block only 429 | - chacha20 7.9 430 | - salsa20 8.0 431 | - norx encrypt 4.5 432 | - norx decrypt 3.7 433 | - norx encrypt 3.9 -- 10mb, 4k messages 434 | - norx decrypt 4.3 -- 10mb, 4k messages 435 | - norx32 encrypt 9.2 436 | - norx32 decrypt 7.9 437 | - morus encrypt 16.8 -- 100mb 438 | - morus decrypt 14.8 -- 100mb 439 | - morus-based xof 10.1 -- 100mb 440 | 441 | -- elliptic curve 442 | 443 | - ec25519 scalarmult 18.9 -- 100 times 444 | 445 | -- base 446 | 447 | - base64 encode 7.1 -- result: 13.3mb 448 | - base64 decode 5.5 -- result: 7.5mb 449 | - base85 encode 4.0 -- result: 12.5mb 450 | - base85 decode 3.8 -- result: 8mb 451 | - base58 encode 13.7 -- 64 bytes x 10,000 452 | - base58 decode 2.4 -- 64 bytes x 10,000 453 | 454 | -- misc 455 | 456 | - adler-32 1.3 457 | - crc-32 1.8 458 | - crc-32 (no table) 5.9 459 | - poly1305 hmac 1.2 460 | - xor1, k16 7.8 461 | - xor8, k16 1.2 462 | 463 | 464 | ]] 465 | --------------------------------------------------------------------------------