diff --git a/.gitignore b/.gitignore index b9d58e2..ebb068d 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,9 @@ cmake-build-*/ bin/ build/ +<<<<<<< HEAD +.vscode/* +assess +======= .vscode/ +>>>>>>> origin/master diff --git a/CMakeLists.txt b/CMakeLists.txt index aae6cd5..416be20 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,4 +21,3 @@ ADD_TEST(test1) ADD_TEST(test2) ADD_TEST(test3) ADD_TEST(test4) - diff --git a/readme.md b/readme.md index 312d950..29bbf8d 100644 --- a/readme.md +++ b/readme.md @@ -212,11 +212,73 @@ $$ ### Security -// TODO +#### Random Number Testing -### Ablation Tests +​ 我们使用NIST提供的 [Statistical Test Suite](https://csrc.nist.gov/Projects/Random-Bit-Generation/Documentation-and-Software) 对哈希结果的随机性进行检测。 -我们在实验框架中分模块编写了单元测试,下面列举一下测试的结果。 +​ 首先生成哈希序列。在 $[0,2^{31}-1)$ 中从零开始均匀选取 $2^{21}$ 个数字,将数字对应的 $32$ 位整数作为输入进行哈希,得到 $20MB$ 输出。使用 NIST Statistical Test Suite 基于多种度量进行随机性测试。默认参数选择 $n=1,500,000$ 可符合大部分测试的要求,按文档[6] 要求使用其他参数的,在下表最后一列注明。 + +| 编号 | 测试类型 | 通过率 | p值均匀性 | 非默认参数 | +| ---- | ------------------------------ | ----------- | ------------ | ------------------------------- | +| 01 | Frequency | 111/111 | 0.580520 | - | +| 02 | Block Frequency | 20758/20971 | 0.273558 | $n=8000,M=80$ | +| 03 | Cumulative Sums | 2/2$^1$ | 通过$^1$ | - | +| 04 | Runs | 111/111 | 0.263452 | - | +| 05 | Longest Run of Ones | 109/111 | 0.656043 | - | +| 06 | Rank | 109/111 | 0.328861 | - | +| 07 | Discrete Fourier Transform | 111/111 | 0.674920 | - | +| 08 | Nonperiodic Template Matchings | 0/148$^1$ | 不通过$^1$ | $n=8000$ | +| 09 | Overlapping Template Matchings | 111/111 | 0.003401 | $m=10$ | +| 10 | Universal Statistical | 54/55 | 0.719747 | $n=3,000,000$,此时 $L=8$ | +| 11 | Approximate Entropy | 110/111 | 0.000086$^2$ | $m=\lfloor \log_2 n \rfloor -6$ | +| 12 | Random Excursions | 8/8$^1$ | 通过$^1$ | - | +| 13 | Random Excursions Variant | 18/18$^1$ | 通过$^1$ | - | +| 14 | Serial | 2/2$^1$ | 通过$^1$ | $m=\lfloor \log_2 n \rfloor -3$ | +| 15 | Linear Complexity | 110/111 | 0.818179 | - | + +注: + +1. 表示该检测度量有多个指标 +2. 表示该指标的 $p$ 值过小,表示 $p$ 值分布不提示均匀分布 + + + + +#### Diffusion Test + +为了度量哈希的扩散效果,我们对 $SHA-RNN$ 算法进行了扩散测试 + +- 输入串 $M$,计算哈希值 $H_1$ ,对于 $M$ 我们选取 Wikipedia 中对海绵结构的介绍 + + > In cryptography, a sponge function or sponge construction is any of a class of algorithms with finite internal state that take an input bit stream of any length and produce an output bit stream of any desired length. Sponge functions have both theoretical and practical uses. They can be used to model or implement many cryptographic primitives, including cryptographic hashes, message authentication codes, mask generation functions, stream ciphers, pseudo-random number generators, and authenticated encryption. + > Sponge functions have both theoretical and practical uses. In theoretical cryptanalysis, a random sponge function is a sponge construction where f is a random permutation or transformation, as appropriate. Random sponge functions capture more of the practical limitations of cryptographic primitives than does the widely used random oracle model, in particular the finite internal state. + +- 对串 $M$ 随机选取一位进行翻转,然后再计算哈希值 $H'$ +- 计算 $H$ 和 $H'$ 中不同有位数的数量 $B_i$ + +将上述过程在 $SHA-RNN$ 算法中重复 $N=10,000$ 次,结果如下图所示 + + + +定量计算 + +1. 改变位数的最小值 + +​ $B_{\min }=\min \left(\left\{B_{i}\right\}_{i=1, \ldots, N}\right)$ + +2. 改变位数的最大值 + $B_{\max }=\max \left(\left\{B_{i}\right\}_{i=1, \ldots, N}\right)$ +3. 改变位数的标准差 + $\Delta B=\sqrt{\frac{1}{N-1} \sum_{i=1}^{N}\left(B_{i}-\bar{B}\right)^{2}}$ +4. 改变位数的平均值 + $\bar{B}=\frac{1}{N} \sum_{i=1}^{N} B_{i}$ +5. 每位的改变概率 + $P=\left(\frac{\bar{B}}{80}\right) \times 100 \%$ + + +| 最小值 | 最大值 | 标准差 | 平均值 | 每位变化概率 | +| ------ | ------ | ------ | ------ | ------------ | +| 21 | 50 | 4.14 | 35.00 | 43.75% | #### DSTMap @@ -237,9 +299,10 @@ $$ [4] Hasler M, Maistrenko Y L. An introduction to the synchronization of chaotic systems: coupled skew tent maps[J]. IEEE Transactions on Circuits and Systems I: Fundamental Theory and Applications, 1997, 44(10): 856-866. - [5] Penard W, van Werkhoven T. On the secure hash algorithm family[J]. Cryptography in context, 2008: 1-18. +[6] Bassham, L. , Rukhin, A. , Soto, J. , Nechvatal, J. , Smid, M. , Leigh, S. , Levenson, M. , Vangel, M. , Heckert, N. and Banks, D. (2010), A Statistical Test Suite for Random and Pseudorandom Number Generators for Cryptographic Applications, Special Publication (NIST SP), National Institute of Standards and Technology, Gaithersburg, MD. + ## Appendix: Introduction to Codebase diff --git a/src/Bitset.hpp b/src/Bitset.hpp index 676ef11..b0e985d 100644 --- a/src/Bitset.hpp +++ b/src/Bitset.hpp @@ -135,6 +135,22 @@ class Bitset { return ss.str(); } + std::string to_bytes() { + std::string ret; + ret.insert(0, (char *)data, n); + return ret; + } + + std::size_t one_count() { + std::size_t count {0}; + for (int i = 0; i < n; i++) { + for (int j = 0; j < 7; j++) { + if (data[i] & (1 << j)) count++; + } + } + return count; + } + private: byte data[n]{}; void init_with(const byte *input, int k) { diff --git a/src/ChaoticSystem.hpp b/src/ChaoticSystem.hpp index fc554c5..ee55b26 100644 --- a/src/ChaoticSystem.hpp +++ b/src/ChaoticSystem.hpp @@ -2,6 +2,7 @@ #include "../include/define.h" #include "ChaoticMap.hpp" +#include "../include/define.h" class ChaoticSystem { public: diff --git a/src/NonLinear.hpp b/src/NonLinear.hpp index 8b5c269..607d5ae 100644 --- a/src/NonLinear.hpp +++ b/src/NonLinear.hpp @@ -1,6 +1,12 @@ #pragma once +#include +#include #include "../include/define.h" +#include +#include + + #include "ChaoticSystem.hpp" template ::type = true> diff --git a/src/PaddedStream.hpp b/src/PaddedStream.hpp index 3c03533..a556697 100644 --- a/src/PaddedStream.hpp +++ b/src/PaddedStream.hpp @@ -1,6 +1,8 @@ #pragma once +#include +#include -#include "../include/define.h" +using byte = uint8_t; // multi-rate padding class PaddedStream { @@ -27,14 +29,6 @@ class PaddedStream { } else { finished = false; } - - // Debug: print the block[0:block_size] to stdout -// for (int i = 0; i < block_size; ++i) { -// std::cout << std::bitset<8>(block[i]) << " "; -// } -// std::cout << std::endl; - - return finished; } diff --git a/src/RNNHash.hpp b/src/RNNHash.hpp index 1867d9d..b1ed33f 100644 --- a/src/RNNHash.hpp +++ b/src/RNNHash.hpp @@ -1,32 +1,32 @@ -#pragma once - -#include "NonLinear.hpp" -#include "RNN.hpp" -#include "SpongeHash.hpp" - -class RNNHash : public SpongeHash<136, 64, 10> { -public: - RNNHash() : SpongeHash(HM_t{}) { - } - ~RNNHash() override = default; - void sponge_F(HM_t &h, uint32_t km) override { - DSTChaoticSystem cs(cs_q, km, cs_ks, cs_us); - RNN<200> rnn{&cs}; - NonLinear<10, 50> nl{nl_nr, &cs}; - - rnn.forward(h, wo); - nl.forward(wo, out); - - for (int i = 0; i < 50; i++) { - h.ptr()[i * 4 + 0] = (out[i] & 0xFF000000) >> 24; - h.ptr()[i * 4 + 1] = (out[i] & 0x00FF0000) >> 16; - h.ptr()[i * 4 + 2] = (out[i] & 0x0000FF00) >> 8; - h.ptr()[i * 4 + 3] = (out[i] & 0x000000FF); - } - } - -private: - const static uint32_t cs_q = 0x789ABCDE, cs_ks = 0x10, cs_us = 10, - nl_nr = 8; - uint32_t wo[10]{}, out[50]{}; +#pragma once + +#include "NonLinear.hpp" +#include "RNN.hpp" +#include "SpongeHash.hpp" + +class RNNHash : public SpongeHash<136, 64, 10> { +public: + RNNHash() : SpongeHash(HM_t{}) { + } + ~RNNHash() override = default; + void sponge_F(HM_t &h, uint32_t km) override { + DSTChaoticSystem cs(cs_q, km, cs_ks, cs_us); + RNN<200> rnn{&cs}; + NonLinear<10, 50> nl{nl_nr, &cs}; + + rnn.forward(h, wo); + nl.forward(wo, out); + + for (int i = 0; i < 50; i++) { + h.ptr()[i * 4 + 0] = (out[i] & 0xFF000000) >> 24; + h.ptr()[i * 4 + 1] = (out[i] & 0x00FF0000) >> 16; + h.ptr()[i * 4 + 2] = (out[i] & 0x0000FF00) >> 8; + h.ptr()[i * 4 + 3] = (out[i] & 0x000000FF); + } + } + +private: + const static uint32_t cs_q = 0x789ABCDE, cs_ks = 0x10, cs_us = 10, + nl_nr = 8; + uint32_t wo[10]{}, out[50]{}; }; \ No newline at end of file diff --git a/src/SpongeHash.hpp b/src/SpongeHash.hpp index d4e2def..98d119c 100644 --- a/src/SpongeHash.hpp +++ b/src/SpongeHash.hpp @@ -1,6 +1,8 @@ #pragma once -#include "../include/define.h" +#include +#include + #include "Bitset.hpp" #include "PaddedStream.hpp" diff --git a/statistics/Makefile b/statistics/Makefile new file mode 100644 index 0000000..48923dd --- /dev/null +++ b/statistics/Makefile @@ -0,0 +1,3 @@ +gen: + g++ sample.cpp -o main + # ./main \ No newline at end of file diff --git a/statistics/analyze.ipynb b/statistics/analyze.ipynb new file mode 100644 index 0000000..7b0c4be --- /dev/null +++ b/statistics/analyze.ipynb @@ -0,0 +1,81 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "4.138176193713967 21 50 35.0032 0.43754\n" + ] + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "with open(\"diffusion.out\", \"r\" , encoding=\"ascii\") as f:\n", + " lst = f.readlines()\n", + "\n", + "lst = list(filter(lambda x: len(x) < 10 and x != '\\n', lst))\n", + "lst = list(map(lambda x: int(x[: -1]), lst))\n", + "\n", + "from matplotlib import pyplot as plt\n", + "import matplotlib as mpl\n", + "import numpy as np\n", + "lst = np.array(lst)\n", + "print(lst.std(ddof=True), lst.min(), lst.max(), lst.mean(), lst.mean() / 80)\n", + "plt.figure(figsize=(10,10) )\n", + "mpl.rcParams[\"font.size\"] = 19\n", + "plt.hist(lst, bins=30,edgecolor='#FFFFFF')\n", + "plt.xlabel(\"Count of changed bits\")\n", + "plt.ylabel(\"Frequency distribution\")\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "interpreter": { + "hash": "ad2bdc8ecc057115af97d19610ffacc2b4e99fae6737bb82f5d7fb13d2f2c186" + }, + "kernelspec": { + "display_name": "Python 3.8.8 ('base')", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.8" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/statistics/defines.h b/statistics/defines.h new file mode 100644 index 0000000..5ab7de8 --- /dev/null +++ b/statistics/defines.h @@ -0,0 +1,2 @@ +const int block_size {136}; +const int filp_count {10000}; \ No newline at end of file diff --git a/statistics/diffusion.cpp b/statistics/diffusion.cpp new file mode 100644 index 0000000..6dcc753 --- /dev/null +++ b/statistics/diffusion.cpp @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include + +#include "../src/RNNHash.hpp" +#include "../include/define.h" + +const int flip_count {10000}; + + +void flip_test() { + std::ifstream s {"text.in", std::ifstream::binary}; + std::ofstream record {"diffusion.out"}; + std::stringstream ssbuf; + ssbuf << s.rdbuf(); + auto stream {ssbuf.str()}; + + auto buf = new byte[stream.size()]; + std::copy(stream.begin(), stream.end(), buf); + std::cerr << buf << std::endl; + + std::random_device dev; + std::mt19937 rng(dev()); + std::uniform_int_distribution byte_dist(0, stream.size() - 1); + std::uniform_int_distribution bit_dist(0, 7); + + RNNHash h; + RNNHash::Out_t original = h(ssbuf); + // record << stream << std::endl; + // record << original.to_binary_string() << std::endl << std::endl; + + + for (int i = 0; i < flip_count; i++) { + auto bit_r {bit_dist(rng)}; + auto byte_r {byte_dist(rng)}; + auto byte_buf = buf + byte_r; + + *byte_buf = ~ ((*byte_buf) | (0xff ^ (1 << bit_r))) | ((*byte_buf) & (0xff ^ (1 << bit_r))); + + std::stringstream input; + std::string new_stream {(char *)buf}; + input.str(new_stream); + + RNNHash hash; + RNNHash::Out_t out = hash(input); + // std::cerr << new_stream << std::endl; + // record << "flip byte: " << byte_r << "; bit: " << bit_r << std::endl; + // record << out.to_binary_string() << std::endl; + out ^= original; + record << out.one_count() << std::endl; + // if (out.one_count() == 0) { + // std::fstream opt {"1.bin"}; + // std::ofstream ori{"2.bin"}; + // opt << new_stream << std::endl; + // ori << stream << std::endl; + // exit(0); + // } + + *byte_buf = ~ ((*byte_buf) | (0xff ^ (1 << bit_r))) | ((*byte_buf) & (0xff ^ (1 << bit_r))); + } + +} + +int main() { + + flip_test(); + + return 0; +} \ No newline at end of file diff --git a/statistics/eval.py b/statistics/eval.py new file mode 100644 index 0000000..d1cf2a8 --- /dev/null +++ b/statistics/eval.py @@ -0,0 +1,84 @@ +import subprocess +import sys +import os +from math import log2, floor +from shutil import copy + +input_file = 'sample.out' + +size = os.path.getsize(input_file) * 8 + +block_size = 80 + +prelude = f""" +0 +{input_file} +0 +""" + +n_size = 1_500_000 + +# adjust the size of n +# according to requirements +# specified in the documentation +n = { + 2: block_size * 100, + 8: block_size * 100, + 10: 3_000_000, +} + +# test parameters +tests = { + 2: f""" + 1 + {block_size} + 0 + """, + 8: f""" + 0 + """, + 9: f""" + 1 + 10 + 0 + """, + 11: f""" + 1 + {floor(log2(n_size)) - 5 - 1} + 0 + """, # m < floor(log_2 n) - 2 + 14: f""" + 1 + {floor(log2(n_size)) - 2 - 1} + 0 + """, # m < floor(log_2 n) - 2 + 15: f""" + 1 + 1000 + 0 + """, +} + +def evaluate(test_id: int): + + choice = "0" * (test_id - 1) + "1" + "0" * (15 - test_id) + num_streams = n[test_id] if test_id in n else n_size + input_s = f""" + {prelude} + {choice} + {tests[test_id] if test_id in tests else ""} + {size // num_streams} + 1 + + """ + print("Total bits:", size) + print("Bits in a single stream:", num_streams) + print("Total Streams:", size // num_streams) + print(input_s) + p = subprocess.run(['./assess', str(num_streams)], input=input_s ,encoding='ascii') + print(p.returncode, p.stdout) + copy("experiments/AlgorithmTesting/finalAnalysisReport.txt", f"report_{test_id}.txt") + +if __name__ == "__main__": + for i in range(1, 16): + evaluate(i) \ No newline at end of file diff --git a/statistics/sample.cpp b/statistics/sample.cpp new file mode 100644 index 0000000..7b5d857 --- /dev/null +++ b/statistics/sample.cpp @@ -0,0 +1,22 @@ +#include +#include + +#include "../src/RNNHash.hpp" +#include "../include/define.h" + +const int k = 21; +const int delta = 1 << (32 - k); + +int main() { + int val {0}; + std::ofstream f {"sample.out"}; + auto p {(char *) &val}; + for (int i = 0; i < (1 << k); ++i) { + std::stringstream s; + s << p[0] << p[1] << p[2] << p[3]; + RNNHash rnn_hash; + auto out = rnn_hash(s); + f << out.to_bytes(); + val += delta; + } +} diff --git a/statistics/text.in b/statistics/text.in new file mode 100644 index 0000000..c6f94ca --- /dev/null +++ b/statistics/text.in @@ -0,0 +1,2 @@ +In cryptography, a sponge function or sponge construction is any of a class of algorithms with finite internal state that take an input bit stream of any length and produce an output bit stream of any desired length. Sponge functions have both theoretical and practical uses. They can be used to model or implement many cryptographic primitives, including cryptographic hashes, message authentication codes, mask generation functions, stream ciphers, pseudo-random number generators, and authenticated encryption. +Sponge functions have both theoretical and practical uses. In theoretical cryptanalysis, a random sponge function is a sponge construction where f is a random permutation or transformation, as appropriate. Random sponge functions capture more of the practical limitations of cryptographic primitives than does the widely used random oracle model, in particular the finite internal state. \ No newline at end of file diff --git a/test/test_diffusion.cpp b/test/test_diffusion.cpp new file mode 100644 index 0000000..d534ef4 --- /dev/null +++ b/test/test_diffusion.cpp @@ -0,0 +1,72 @@ +#include +#include +#include +#include +#include +#include + +#include "RNNHash.hpp" +#include "define.h" + +const int flip_count {10000}; + + +void flip_test() { + std::ifstream s {"text.in", std::ifstream::binary}; + std::ofstream record {"diffusion.out"}; + std::stringstream ssbuf; + ssbuf << s.rdbuf(); + auto stream {ssbuf.str()}; + + auto buf = new byte[stream.size()]; + std::copy(stream.begin(), stream.end(), buf); + std::cerr << buf << std::endl; + + std::random_device dev; + std::mt19937 rng(dev()); + std::uniform_int_distribution byte_dist(0, stream.size() - 1); + std::uniform_int_distribution bit_dist(0, 7); + + RNNHash h; + RNNHash::Out_t original = h(ssbuf); + // record << stream << std::endl; + // record << original.to_binary_string() << std::endl << std::endl; + + + for (int i = 0; i < flip_count; i++) { + auto bit_r {bit_dist(rng)}; + auto byte_r {byte_dist(rng)}; + auto byte_buf = buf + byte_r; + + *byte_buf = ~ ((*byte_buf) | (0xff ^ (1 << bit_r))) | ((*byte_buf) & (0xff ^ (1 << bit_r))); + + std::stringstream input; + std::string new_stream {(char *)buf}; + input.str(new_stream); + + RNNHash hash; + RNNHash::Out_t out = hash(input); + // std::cerr << new_stream << std::endl; + // record << "flip byte: " << byte_r << "; bit: " << bit_r << std::endl; + // record << out.to_binary_string() << std::endl; + out ^= original; + record << out.one_count() << std::endl; + // if (out.one_count() == 0) { + // std::fstream opt {"1.bin"}; + // std::ofstream ori{"2.bin"}; + // opt << new_stream << std::endl; + // ori << stream << std::endl; + // exit(0); + // } + + *byte_buf = ~ ((*byte_buf) | (0xff ^ (1 << bit_r))) | ((*byte_buf) & (0xff ^ (1 << bit_r))); + } + +} + +int main() { + + flip_test(); + + return 0; +} \ No newline at end of file