├── .github ├── CODEOWNERS └── workflows │ └── run_tests.yml ├── .gitignore ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── benchmark.sh ├── cmd ├── homo │ └── main.go └── tzsum │ └── main.go ├── demo.sh ├── gf127 ├── doc.go ├── gf127.go ├── gf127_amd64.go ├── gf127_amd64.s ├── gf127_generic.go ├── gf127_test.go ├── gf127x2.go ├── gf127x2_amd64.go ├── gf127x2_amd64.s ├── gf127x2_generic.go └── gf127x2_test.go ├── go.mod ├── go.sum ├── help.mk └── tz ├── digest.go ├── digest_avx2_amd64.s ├── digest_avx_amd64.s ├── digest_generic.go ├── digets_amd64.go ├── hash.go ├── hash_test.go ├── sl2.go └── sl2_test.go /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @TrueCloudLab/storage-core @TrueCloudLab/storage-services @TrueCloudLab/committers 2 | -------------------------------------------------------------------------------- /.github/workflows/run_tests.yml: -------------------------------------------------------------------------------- 1 | name: Tests 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | types: [ opened, synchronize ] 8 | workflow_dispatch: 9 | 10 | jobs: 11 | lint: 12 | name: Lint 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: golangci-lint 18 | uses: golangci/golangci-lint-action@v2 19 | with: 20 | version: latest 21 | 22 | test_cover: 23 | name: Coverage 24 | runs-on: ubuntu-20.04 25 | 26 | env: 27 | CGO_ENABLED: 0 28 | steps: 29 | - uses: actions/checkout@v2 30 | with: 31 | fetch-depth: 0 32 | 33 | - name: Set up Go 34 | uses: actions/setup-go@v2 35 | with: 36 | go-version: 1.18 37 | 38 | - name: Restore Go modules from cache 39 | uses: actions/cache@v2 40 | with: 41 | path: /home/runner/go/pkg/mod 42 | key: deps-${{ hashFiles('go.sum') }} 43 | 44 | - name: Update Go modules 45 | run: go mod download -json 46 | 47 | - name: Write coverage profile 48 | run: go test -v ./... -coverprofile=./coverage.txt -covermode=atomic -coverpkg=./pkg...,./cli/... 49 | 50 | - name: Upload coverage results to Codecov 51 | uses: codecov/codecov-action@v1 52 | with: 53 | fail_ci_if_error: false 54 | path_to_write_report: ./coverage.txt 55 | verbose: true 56 | 57 | tests: 58 | name: Run tests 59 | runs-on: ${{ matrix.os }} 60 | strategy: 61 | matrix: 62 | os: [ ubuntu-20.04, windows-2022 ] 63 | go_versions: [ '1.16', '1.17', '1.18' ] 64 | exclude: 65 | - os: windows-2022 66 | go_versions: '1.16' 67 | - os: windows-2022 68 | go_versions: '1.17' 69 | - os: ubuntu-20.04 70 | go_versions: '1.18' 71 | fail-fast: false 72 | steps: 73 | - uses: actions/checkout@v2 74 | with: 75 | fetch-depth: 0 76 | 77 | - name: Set up Go 78 | uses: actions/setup-go@v2 79 | with: 80 | go-version: '${{ matrix.go_versions }}' 81 | 82 | - name: Restore Go modules from cache 83 | uses: actions/cache@v2 84 | with: 85 | path: /home/runner/go/pkg/mod 86 | key: deps-${{ hashFiles('go.sum') }} 87 | 88 | - name: Update Go modules 89 | run: go mod download -json 90 | 91 | - name: Run tests 92 | run: go test -v ./... 93 | 94 | - name: Run generic tests 95 | run: go test -v -count=1 ./... --tags=generic 96 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE 2 | .idea 3 | .vscode 4 | 5 | # Vendoring 6 | vendor 7 | 8 | # tempfiles 9 | .DS_Store 10 | *~ 11 | .cache 12 | 13 | temp 14 | tmp 15 | 16 | # binary 17 | bin/ 18 | release/ 19 | 20 | # coverage 21 | coverage.txt 22 | coverage.html 23 | 24 | # testing 25 | cmd/test 26 | /plugins/ 27 | testfile 28 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution guide 2 | 3 | First, thank you for contributing! We love and encourage pull requests from 4 | everyone. Please follow the guidelines: 5 | 6 | - Check the open [issues](https://github.com/TrueCloudLab/tzhash/issues) and 7 | [pull requests](https://github.com/TrueCloudLab/tzhash/pulls) for existing 8 | discussions. 9 | 10 | - Open an issue first, to discuss a new feature or enhancement. 11 | 12 | - Write tests, and make sure the test suite passes locally and on CI. 13 | 14 | - Open a pull request, and reference the relevant issue(s). 15 | 16 | - Make sure your commits are logically separated and have good comments 17 | explaining the details of your change. 18 | 19 | - After receiving feedback, amend your commits or add new ones as 20 | appropriate. 21 | 22 | - **Have fun!** 23 | 24 | ## Development Workflow 25 | 26 | Start by forking the `tzhash` repository, make changes in a branch and then 27 | send a pull request. We encourage pull requests to discuss code changes. Here 28 | are the steps in details: 29 | 30 | ### Set up your GitHub Repository 31 | Fork [TZHash upstream](https://github.com/TrueCloudLab/tzhash/fork) source 32 | repository to your own personal repository. Copy the URL of your fork (you will 33 | need it for the `git clone` command below). 34 | 35 | ```sh 36 | $ git clone https://github.com/TrueCloudLab/tzhash 37 | ``` 38 | 39 | ### Set up git remote as ``upstream`` 40 | ```sh 41 | $ cd tzhash 42 | $ git remote add upstream https://github.com/TrueCloudLab/tzhash 43 | $ git fetch upstream 44 | $ git merge upstream/master 45 | ... 46 | ``` 47 | 48 | ### Create your feature branch 49 | Before making code changes, make sure you create a separate branch for these 50 | changes. Maybe you will find it convenient to name branch in 51 | `/-` format. 52 | 53 | ``` 54 | $ git checkout -b feature/123-something_awesome 55 | ``` 56 | 57 | ### Test your changes 58 | After your code changes, make sure 59 | 60 | - To add test cases for the new code. 61 | - To squash your commits into a single commit or a series of logically separated 62 | commits run `git rebase -i`. It's okay to force update your pull request. 63 | - To run `make test` and `make all` completes. 64 | 65 | ### Commit changes 66 | After verification, commit your changes. This is a [great 67 | post](https://chris.beams.io/posts/git-commit/) on how to write useful commit 68 | messages. Try following this template: 69 | 70 | ``` 71 | [#Issue] Summary 72 | 73 | Description 74 | 75 | 76 | 77 | 78 | ``` 79 | 80 | ``` 81 | $ git commit -sam '[#123] Add some feature' 82 | ``` 83 | 84 | ### Push to the branch 85 | Push your locally committed changes to the remote origin (your fork) 86 | ``` 87 | $ git push origin feature/123-something_awesome 88 | ``` 89 | 90 | ### Create a Pull Request 91 | Pull requests can be created via GitHub. Refer to [this 92 | document](https://help.github.com/articles/creating-a-pull-request/) for 93 | detailed steps on how to create a pull request. After a Pull Request gets peer 94 | reviewed and approved, it will be merged. 95 | 96 | ## DCO Sign off 97 | 98 | All authors to the project retain copyright to their work. However, to ensure 99 | that they are only submitting work that they have rights to, we are requiring 100 | everyone to acknowledge this by signing their work. 101 | 102 | Any copyright notices in this repository should specify the authors as "the 103 | contributors". 104 | 105 | To sign your work, just add a line like this at the end of your commit message: 106 | 107 | ``` 108 | Signed-off-by: Samii Sakisaka 109 | 110 | ``` 111 | 112 | This can easily be done with the `--signoff` option to `git commit`. 113 | 114 | By doing this you state that you can certify the following (from [The Developer 115 | Certificate of Origin](https://developercertificate.org/)): 116 | 117 | ``` 118 | Developer Certificate of Origin 119 | Version 1.1 120 | 121 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 122 | 1 Letterman Drive 123 | Suite D4700 124 | San Francisco, CA, 94129 125 | 126 | Everyone is permitted to copy and distribute verbatim copies of this 127 | license document, but changing it is not allowed. 128 | 129 | 130 | Developer's Certificate of Origin 1.1 131 | 132 | By making a contribution to this project, I certify that: 133 | 134 | (a) The contribution was created in whole or in part by me and I 135 | have the right to submit it under the open source license 136 | indicated in the file; or 137 | 138 | (b) The contribution is based upon previous work that, to the best 139 | of my knowledge, is covered under an appropriate open source 140 | license and I have the right under that license to submit that 141 | work with modifications, whether created in whole or in part 142 | by me, under the same open source license (unless I am 143 | permitted to submit under a different license), as indicated 144 | in the file; or 145 | 146 | (c) The contribution was provided directly to me by some other 147 | person who certified (a), (b) or (c) and I have not modified 148 | it. 149 | 150 | (d) I understand and agree that this project and the contribution 151 | are public and that a record of the contribution (including all 152 | personal information I submit with it, including my sign-off) is 153 | maintained indefinitely and may be redistributed consistent with 154 | this project or the open source license(s) involved. 155 | ``` 156 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1-alpine as builder 2 | 3 | RUN set -x \ 4 | && apk add --no-cache \ 5 | git \ 6 | curl \ 7 | && mkdir -p /tmp \ 8 | && mkdir -p /fixtures \ 9 | && curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/01.txt \ 10 | && curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/02.txt \ 11 | && curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/03.txt \ 12 | && curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/04.txt \ 13 | && curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/05.txt \ 14 | && curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/06.txt \ 15 | && curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/07.txt \ 16 | && curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/08.txt \ 17 | && curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/09.txt \ 18 | && curl -s https://loripsum.net/api/1/verylong/plaintext | awk 'NF' - | cat > /fixtures/10.txt 19 | 20 | COPY . /tzhash 21 | 22 | WORKDIR /tzhash 23 | 24 | # https://github.com/golang/go/wiki/Modules#how-do-i-use-vendoring-with-modules-is-vendoring-going-away 25 | # go build -mod=vendor 26 | RUN set -x \ 27 | && export CGO_ENABLED=0 \ 28 | && go build -mod=vendor -o /go/bin/homo ./cmd/homo/main.go 29 | 30 | # Executable image 31 | FROM alpine:3.11 32 | 33 | WORKDIR /fixtures 34 | 35 | COPY --from=builder /fixtures /fixtures 36 | COPY --from=builder /go/bin/homo /usr/local/sbin/homo 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | SHELL = bash 3 | 4 | REPO ?= $(shell go list -m) 5 | VERSION ?= $(shell git describe --tags --dirty --match "v*" --always --abbrev=8 2>/dev/null || cat VERSION 2>/dev/null || echo "develop") 6 | 7 | BIN = bin 8 | DIRS = $(BIN) 9 | 10 | # List of binaries to build. 11 | CMDS = $(notdir $(basename $(wildcard cmd/*))) 12 | BINS = $(addprefix $(BIN)/, $(CMDS)) 13 | 14 | .PHONY: all help clean 15 | 16 | # To build a specific binary, use it's name prefix with bin/ as a target 17 | # For example `make bin/tzsum` will build only storage node binary 18 | # Just `make` will build all possible binaries 19 | all: $(DIRS) $(BINS) 20 | 21 | # help target 22 | include help.mk 23 | 24 | $(BINS): $(DIRS) dep 25 | @echo "⇒ Build $@" 26 | CGO_ENABLED=0 \ 27 | go build -v -trimpath \ 28 | -ldflags "-X $(REPO)/misc.Version=$(VERSION)" \ 29 | -o $@ ./cmd/$(notdir $@) 30 | 31 | $(DIRS): 32 | @echo "⇒ Ensure dir: $@" 33 | @mkdir -p $@ 34 | 35 | # Pull go dependencies 36 | dep: 37 | @printf "⇒ Download requirements: " 38 | CGO_ENABLED=0 \ 39 | go mod download && echo OK 40 | @printf "⇒ Tidy requirements : " 41 | CGO_ENABLED=0 \ 42 | go mod tidy -v && echo OK 43 | 44 | # Run Unit Test with go test 45 | test: 46 | @echo "⇒ Running go test" 47 | @go test ./... 48 | 49 | # Run Unit Test with go test 50 | test.generic: 51 | @echo "⇒ Running go test with generic tag" 52 | @go test ./... --tags=generic 53 | 54 | # Print version 55 | version: 56 | @echo $(VERSION) 57 | 58 | clean: 59 | rm -rf vendor 60 | rm -rf .cache 61 | rm -rf $(BIN) 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Demo 2 | 3 | [![asciicast](https://asciinema.org/a/IArEDLTrQyabI3agSSpINoqNu.svg)](https://asciinema.org/a/IArEDLTrQyabI3agSSpINoqNu) 4 | 5 | **In project root:** 6 | 7 | ```bash 8 | $ make 9 | ... 10 | $ ./demo.sh 11 | 12 | ``` 13 | 14 | # Homomorphic hashing in golang 15 | 16 | Package `tz` contains pure-Go (with some Assembly) implementation of hashing 17 | function described by [Tillich and 18 | Zémor](https://link.springer.com/content/pdf/10.1007/3-540-48658-5_5.pdf). 19 | 20 | There are [existing implementations](https://github.com/srijs/hwsl2-core) 21 | already, however they are written in C. 22 | 23 | Package `gf127` contains arithmetic in `GF(2^127)` with `x^127+x^63+1` as reduction polynomial. 24 | 25 | # Description 26 | 27 | TZ Hash can be used instead of Merkle-tree for data-validation, because 28 | homomorphic hashes are concatenable: hash sum of data can be calculated based on 29 | hashes of chunks. 30 | 31 | The example of how it works can be seen in tests and demo. 32 | 33 | # Benchmarks 34 | 35 | ## go vs AVX vs AVX2 version 36 | 37 | ``` 38 | BenchmarkSum/AVX_digest-8 308 3889484 ns/op 25.71 MB/s 5 allocs/op 39 | BenchmarkSum/AVXInline_digest-8 457 2455437 ns/op 40.73 MB/s 5 allocs/op 40 | BenchmarkSum/AVX2_digest-8 399 3031102 ns/op 32.99 MB/s 3 allocs/op 41 | BenchmarkSum/AVX2Inline_digest-8 602 2077719 ns/op 48.13 MB/s 3 allocs/op 42 | BenchmarkSum/PureGo_digest-8 68 17795480 ns/op 5.62 MB/s 5 allocs/op 43 | ``` 44 | 45 | # Makefile 46 | 47 | ``` bash 48 | Usage: 49 | 50 | make 51 | 52 | Targets: 53 | 54 | all Just `make` will build all possible binaries 55 | clean Print version 56 | dep Pull go dependencies 57 | help Show this help prompt 58 | test Run Unit Test with go test 59 | version Print version 60 | ``` 61 | 62 | # Contributing 63 | 64 | Feel free to contribute to this project after reading the [contributing 65 | guidelines](CONTRIBUTING.md). 66 | 67 | Before starting to work on a certain topic, create a new issue first, describing 68 | the feature/topic you are going to implement. 69 | 70 | # License 71 | 72 | This project is licensed under the Apache 2.0 License - 73 | see the [LICENSE](LICENSE) file for details 74 | 75 | # References 76 | 77 | - [https://link.springer.com/content/pdf/10.1007/3-540-48658-5_5.pdf](https://link.springer.com/content/pdf/10.1007/3-540-48658-5_5.pdf) 78 | - [https://github.com/srijs/hwsl2-core](https://github.com/srijs/hwsl2-core) 79 | -------------------------------------------------------------------------------- /benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | BLOCK_SIZE=${1:-1G} # gigabyte by default 4 | OUT="${OUT:-$(mktemp /tmp/random-file.XXXXXX)}" 5 | 6 | dd if=/dev/urandom of="$OUT" bs="$BLOCK_SIZE" count=1 7 | 8 | for impl in avx avx2 generic; do 9 | echo $impl implementation: 10 | time ./bin/tzsum -name "$OUT" -impl $impl 11 | echo 12 | done 13 | -------------------------------------------------------------------------------- /cmd/homo/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "encoding/hex" 6 | "flag" 7 | "fmt" 8 | "os" 9 | 10 | "github.com/TrueCloudLab/tzhash/tz" 11 | ) 12 | 13 | var ( 14 | concat = flag.Bool("concat", false, "Concatenate hashes") 15 | filename = flag.String("file", "", "File to read from") 16 | ) 17 | 18 | func main() { 19 | var ( 20 | err error 21 | file = os.Stdin 22 | lines = make([]string, 0, 10) 23 | ) 24 | 25 | flag.Parse() 26 | if *filename != "" { 27 | if file, err = os.Open(*filename); err != nil { 28 | fatal("error while opening file: %v", err) 29 | } 30 | } 31 | 32 | for f := bufio.NewScanner(file); f.Scan(); { 33 | lines = append(lines, f.Text()) 34 | } 35 | 36 | if *concat { 37 | var ( 38 | h []byte 39 | hashes = make([][]byte, len(lines)) 40 | ) 41 | for i := range lines { 42 | if hashes[i], err = hex.DecodeString(lines[i]); err != nil { 43 | fatal("error while decoding hex-string: %v", err) 44 | } 45 | } 46 | h, err := tz.Concat(hashes) 47 | if err != nil { 48 | fatal("error while concatenating hashes: %v", err) 49 | } 50 | fmt.Println(hex.EncodeToString(h)) 51 | return 52 | } 53 | 54 | for i := range lines { 55 | h := tz.Sum([]byte(lines[i])) 56 | fmt.Println(hex.EncodeToString(h[:])) 57 | } 58 | } 59 | 60 | func fatal(msg string, args ...interface{}) { 61 | fmt.Printf(msg+"\n", args...) 62 | os.Exit(1) 63 | } 64 | -------------------------------------------------------------------------------- /cmd/tzsum/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "hash" 7 | "io" 8 | "log" 9 | "os" 10 | "runtime" 11 | "runtime/pprof" 12 | 13 | "github.com/TrueCloudLab/tzhash/tz" 14 | "golang.org/x/sys/cpu" 15 | ) 16 | 17 | var ( 18 | cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") 19 | memprofile = flag.String("memprofile", "", "write memory profile to `file`") 20 | filename = flag.String("name", "-", "file to use") 21 | hashimpl = flag.String("impl", "", "implementation to use") 22 | ) 23 | 24 | func main() { 25 | var ( 26 | f io.Reader 27 | err error 28 | ) 29 | 30 | flag.Parse() 31 | if *cpuprofile != "" { 32 | f, err := os.Create(*cpuprofile) 33 | if err != nil { 34 | log.Fatal("could not create CPU profile: ", err) 35 | } 36 | if err := pprof.StartCPUProfile(f); err != nil { 37 | log.Fatal("could not start CPU profile: ", err) 38 | } 39 | defer pprof.StopCPUProfile() 40 | } 41 | 42 | if *filename != "-" { 43 | if f, err = os.Open(*filename); err != nil { 44 | log.Fatal("could not open file: ", err) 45 | } 46 | } else { 47 | f = os.Stdin 48 | } 49 | 50 | // Override CPU feature flags to make sure a proper backend is used. 51 | var h hash.Hash 52 | switch *hashimpl { 53 | case "avx": 54 | cpu.X86.HasAVX = true 55 | cpu.X86.HasAVX2 = false 56 | h = tz.New() 57 | case "avx2": 58 | cpu.X86.HasAVX = true 59 | cpu.X86.HasAVX2 = true 60 | h = tz.New() 61 | case "generic": 62 | cpu.X86.HasAVX = false 63 | cpu.X86.HasAVX2 = false 64 | h = tz.New() 65 | default: 66 | log.Fatalf("Invalid backend: %s", *hashimpl) 67 | } 68 | 69 | if _, err := io.Copy(h, f); err != nil { 70 | log.Fatal("error while reading file: ", err) 71 | } 72 | fmt.Printf("%x\t%s\n", h.Sum(nil), *filename) 73 | 74 | if *memprofile != "" { 75 | f, err := os.Create(*memprofile) 76 | if err != nil { 77 | log.Fatal("could not create memory profile: ", err) 78 | } 79 | runtime.GC() // get up-to-date statistics 80 | if err := pprof.WriteHeapProfile(f); err != nil { 81 | log.Fatal("could not write memory profile: ", err) 82 | } 83 | f.Close() 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /demo.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | #set -x 3 | 4 | BLOCK_SIZE=${1:-100M} # 100Mb by default 5 | TMPDIR="${TMPDIR:-$(mktemp -d)}" 6 | 7 | OUT="${OUT:-"${TMPDIR}/bighash"}" 8 | 9 | echo "Preparing big file at ${OUT}..." 10 | dd if=/dev/urandom of="$OUT" bs="$BLOCK_SIZE" count=1 11 | 12 | echo "Make 4 smaller parts from ${OUT}..." 13 | split -dn 4 "${OUT}" "${TMPDIR}/" 14 | 15 | echo -n "Big file hash: " 16 | TZALL=$(./bin/tzsum -impl avx2 -name "${OUT}" | awk '{print $1}') 17 | echo "${TZALL}" 18 | 19 | for i in $(seq -f "%02g" 0 3) 20 | do 21 | echo -n "Part ${i} hash: " 22 | PART=$(./bin/tzsum -impl avx2 -name "${TMPDIR}/${i}" | awk '{print $1}') 23 | echo "${PART}" | tee -a "${TMPDIR}/part.hashes" 24 | done 25 | 26 | echo -n "Cumulative: " 27 | TZCUM=$(./bin/homo -concat -file "${TMPDIR}/part.hashes") 28 | echo "${TZCUM}" 29 | 30 | if [[ "$TZCUM" == "$TZALL" ]]; then 31 | echo "Original and cumulative hashes are equal!" 32 | else 33 | echo "Original and cumulative hashes are NOT equal!" 34 | fi 35 | 36 | echo -ne "Cleaning up .. " 37 | rm -rf "${TMPDIR}" 38 | echo "Done!" 39 | -------------------------------------------------------------------------------- /gf127/doc.go: -------------------------------------------------------------------------------- 1 | // Package gf127 implements the GF(2^127) arithmetic 2 | // modulo reduction polynomial x^127 + x^63 + 1 . 3 | // gf127.go contains common definitions. 4 | // Other files contain architecture-specific implementations. 5 | // 6 | // Copyright 2019 (c) NSPCC 7 | package gf127 8 | -------------------------------------------------------------------------------- /gf127/gf127.go: -------------------------------------------------------------------------------- 1 | package gf127 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | "math/bits" 8 | "math/rand" 9 | ) 10 | 11 | // GF127 represents element of GF(2^127) 12 | type GF127 [2]uint64 13 | 14 | const ( 15 | byteSize = 16 16 | maxUint64 = ^uint64(0) 17 | msb64 = uint64(1) << 63 18 | ) 19 | 20 | // x127x631 is reduction polynomial x^127 + x^63 + 1 21 | var x127x631 = GF127{msb64 + 1, msb64} 22 | 23 | // New constructs new element of GF(2^127) as hi*x^64 + lo. 24 | // It is assumed that hi has zero MSB. 25 | func New(lo, hi uint64) *GF127 { 26 | return &GF127{lo, hi} 27 | } 28 | 29 | func addGeneric(a, b, c *GF127) { 30 | c[0] = a[0] ^ b[0] 31 | c[1] = a[1] ^ b[1] 32 | } 33 | 34 | func mulGeneric(a, b, c *GF127) { 35 | r := new(GF127) 36 | d := *a 37 | for i := uint(0); i < 64; i++ { 38 | if b[0]&(1<> 63 54 | b[0] = a[0] << 1 55 | b[1] = (a[1] << 1) ^ c 56 | 57 | mask := b[1] & msb64 58 | b[0] ^= mask | (mask >> 63) 59 | b[1] ^= mask 60 | } 61 | 62 | func mul11Generic(a, b *GF127) { 63 | c := a[0] >> 63 64 | b[0] = a[0] ^ (a[0] << 1) 65 | b[1] = a[1] ^ (a[1] << 1) ^ c 66 | 67 | mask := b[1] & msb64 68 | b[0] ^= mask | (mask >> 63) 69 | b[1] ^= mask 70 | } 71 | 72 | // Inv sets b to a^-1 73 | // Algorithm is based on Extended Euclidean Algorithm 74 | // and is described by Hankerson, Hernandez, Menezes in 75 | // https://link.springer.com/content/pdf/10.1007/3-540-44499-8_1.pdf 76 | func Inv(a, b *GF127) { 77 | var ( 78 | v = x127x631 79 | u = *a 80 | c, d = &GF127{1, 0}, &GF127{0, 0} 81 | t = new(GF127) 82 | x *GF127 83 | ) 84 | 85 | // degree of polynomial is a position of most significant bit 86 | for du, dv := msb(&u), msb(&v); du != 0; du, dv = msb(&u), msb(&v) { 87 | if du < dv { 88 | v, u = u, v 89 | dv, du = du, dv 90 | d, c = c, d 91 | } 92 | 93 | x = xN(du - dv) 94 | 95 | Mul(x, &v, t) 96 | Add(&u, t, &u) 97 | 98 | // becasuse mulAVX performs reduction on t, we need 99 | // manually reduce u at first step 100 | if msb(&u) == 127 { 101 | Add(&u, &x127x631, &u) 102 | } 103 | 104 | Mul(x, d, t) 105 | Add(c, t, c) 106 | } 107 | *b = *c 108 | } 109 | 110 | func xN(n int) *GF127 { 111 | if n < 64 { 112 | return &GF127{1 << uint(n), 0} 113 | } 114 | return &GF127{0, 1 << uint(n-64)} 115 | } 116 | 117 | func msb(a *GF127) (x int) { 118 | x = bits.LeadingZeros64(a[1]) 119 | if x == 64 { 120 | x = bits.LeadingZeros64(a[0]) + 64 121 | } 122 | return 127 - x 123 | } 124 | 125 | // Mul1 copies b into a. 126 | func Mul1(a, b *GF127) { 127 | a[0] = b[0] 128 | a[1] = b[1] 129 | } 130 | 131 | // And sets c to a & b (bitwise-and). 132 | func And(a, b, c *GF127) { 133 | c[0] = a[0] & b[0] 134 | c[1] = a[1] & b[1] 135 | } 136 | 137 | // Random returns random element from GF(2^127). 138 | // Is used mostly for testing. 139 | func Random() *GF127 { 140 | return &GF127{rand.Uint64(), rand.Uint64() >> 1} 141 | } 142 | 143 | // String returns hex-encoded representation, starting with MSB. 144 | func (c *GF127) String() string { 145 | buf := c.Bytes() 146 | return hex.EncodeToString(buf[:]) 147 | } 148 | 149 | // Equals checks if two reduced (zero MSB) elements of GF(2^127) are equal 150 | func (c *GF127) Equals(b *GF127) bool { 151 | return c[0] == b[0] && c[1] == b[1] 152 | } 153 | 154 | // Bytes represents element of GF(2^127) as byte array of length 16. 155 | func (c *GF127) Bytes() [16]byte { 156 | var buf [16]byte 157 | binary.BigEndian.PutUint64(buf[:8], c[1]) 158 | binary.BigEndian.PutUint64(buf[8:], c[0]) 159 | return buf 160 | } 161 | 162 | // MarshalBinary implements encoding.BinaryMarshaler. 163 | func (c *GF127) MarshalBinary() (data []byte, err error) { 164 | buf := c.Bytes() 165 | return buf[:], nil 166 | } 167 | 168 | // UnmarshalBinary implements encoding.BinaryUnmarshaler. 169 | func (c *GF127) UnmarshalBinary(data []byte) error { 170 | if len(data) != byteSize { 171 | return errors.New("data must be 16-bytes long") 172 | } 173 | 174 | c[0] = binary.BigEndian.Uint64(data[8:]) 175 | c[1] = binary.BigEndian.Uint64(data[:8]) 176 | if c[1]&msb64 != 0 { 177 | return errors.New("MSB must be zero") 178 | } 179 | 180 | return nil 181 | } 182 | -------------------------------------------------------------------------------- /gf127/gf127_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build amd64 && !generic 2 | // +build amd64,!generic 3 | 4 | // Package gf127 implements the GF(2^127) arithmetic 5 | // modulo reduction polynomial x^127 + x^63 + 1 . 6 | // This is rather straight-forward re-implementation of C library 7 | // available here https://github.com/srijs/hwsl2-core . 8 | // Interfaces are highly influenced by math/big . 9 | package gf127 10 | 11 | import "golang.org/x/sys/cpu" 12 | 13 | // x127x63 represents x^127 + x^63 14 | var x127x63 = GF127{msb64, msb64} //nolint:deadcode,varcheck 15 | 16 | // Add sets c to a+b. 17 | func Add(a, b, c *GF127) { 18 | if cpu.X86.HasAVX { 19 | addAVX(a, b, c) 20 | } else { 21 | addGeneric(a, b, c) 22 | } 23 | } 24 | 25 | // Mul sets c to a*b. 26 | func Mul(a, b, c *GF127) { 27 | if cpu.X86.HasAVX { 28 | mulAVX(a, b, c) 29 | } else { 30 | mulGeneric(a, b, c) 31 | } 32 | } 33 | 34 | // Mul10 sets b to a*x. 35 | func Mul10(a, b *GF127) { 36 | if cpu.X86.HasAVX { 37 | mul10AVX(a, b) 38 | } else { 39 | mul10Generic(a, b) 40 | } 41 | } 42 | 43 | // Mul11 sets b to a*(x+1). 44 | func Mul11(a, b *GF127) { 45 | if cpu.X86.HasAVX { 46 | mul11AVX(a, b) 47 | } else { 48 | mul11Generic(a, b) 49 | } 50 | } 51 | 52 | func addAVX(a, b, c *GF127) 53 | func mulAVX(a, b, c *GF127) 54 | func mul10AVX(a, b *GF127) 55 | func mul11AVX(a, b *GF127) 56 | -------------------------------------------------------------------------------- /gf127/gf127_amd64.s: -------------------------------------------------------------------------------- 1 | #include "textflag.h" 2 | 3 | // func Add(a, b, c *[2]uint64) 4 | TEXT ·addAVX(SB), NOSPLIT, $0 5 | MOVQ a+0(FP), AX 6 | MOVUPD (AX), X0 7 | MOVQ b+8(FP), BX 8 | MOVUPD (BX), X1 9 | XORPD X1, X0 10 | MOVQ c+16(FP), CX 11 | MOVUPD X0, (CX) 12 | RET 13 | 14 | // func Mul10(a, b *[2]uint64) 15 | TEXT ·mul10AVX(SB), NOSPLIT, $0 16 | MOVQ a+0(FP), AX 17 | MOVUPD (AX), X0 18 | VPSLLQ $1, X0, X1 19 | VPALIGNR $8, X1, X0, X2 20 | PSRLQ $63, X2 21 | MOVUPD ·x127x63(SB), X3 22 | ANDPD X1, X3 23 | VPUNPCKHQDQ X3, X3, X3 24 | XORPD X2, X1 25 | XORPD X3, X1 26 | MOVQ b+8(FP), AX 27 | MOVUPD X1, (AX) 28 | RET 29 | 30 | // func Mul11(a, b *[2]uint64) 31 | TEXT ·mul11AVX(SB), NOSPLIT, $0 32 | MOVQ a+0(FP), AX 33 | MOVUPD (AX), X0 34 | VPSLLQ $1, X0, X1 35 | VPALIGNR $8, X1, X0, X2 36 | PSRLQ $63, X2 37 | MOVUPD ·x127x63(SB), X3 38 | ANDPD X1, X3 39 | VPUNPCKHQDQ X3, X3, X3 40 | XORPD X2, X1 41 | XORPD X3, X1 42 | XORPD X0, X1 43 | MOVQ b+8(FP), AX 44 | MOVUPD X1, (AX) 45 | RET 46 | 47 | // func Mul(a, b, c *[2]uint64) 48 | TEXT ·mulAVX(SB), NOSPLIT, $0 49 | MOVQ a+0(FP), AX // X0 = a0 . a1 50 | MOVUPD (AX), X0 // X0 = a0 . a1 51 | MOVQ b+8(FP), BX // X1 = b0 . b1 52 | MOVUPD (BX), X1 // X1 = b0 . b1 53 | VPUNPCKLQDQ X1, X0, X2 // X2 = a0 . b0 54 | VPUNPCKHQDQ X1, X0, X3 // X3 = a1 . b1 55 | XORPD X2, X3 // X3 = (a0 + a1) . (b0 + b1) 56 | PCLMULQDQ $0x10, X3, X3 // X3 = (a0 + a1) * (b0 + b1) 57 | VPCLMULQDQ $0x00, X0, X1, X4 // X4 = a0 * b0 58 | VPCLMULQDQ $0x11, X0, X1, X5 // X5 = a1 * b1 59 | XORPD X4, X3 60 | XORPD X5, X3 // X3 = a0 * b1 + a1 * b0 61 | VPSLLDQ $8, X3, X2 62 | XORPD X2, X4 // X4 = a0 * b0 + lo(X3) 63 | VPSRLDQ $8, X3, X6 64 | XORPD X6, X5 // X5 = a1 * b1 + hi(X3) 65 | 66 | // at this point, a * b = X4 . X5 (as 256-bit number) 67 | // reduction modulo x^127 + x^63 + 1 68 | VPALIGNR $8, X4, X5, X3 69 | XORPD X5, X3 70 | PSLLQ $1, X5 71 | XORPD X5, X4 72 | VPUNPCKHQDQ X3, X5, X5 73 | XORPD X5, X4 74 | PSRLQ $63, X3 75 | XORPD X3, X4 76 | VPUNPCKLQDQ X3, X3, X5 77 | PSLLQ $63, X5 78 | XORPD X5, X4 79 | MOVQ c+16(FP), CX 80 | MOVUPD X4, (CX) 81 | RET 82 | -------------------------------------------------------------------------------- /gf127/gf127_generic.go: -------------------------------------------------------------------------------- 1 | //go:build !amd64 || generic 2 | // +build !amd64 generic 3 | 4 | package gf127 5 | 6 | // Add sets c to a+b. 7 | func Add(a, b, c *GF127) { 8 | addGeneric(a, b, c) 9 | } 10 | 11 | // Mul sets c to a*b. 12 | func Mul(a, b, c *GF127) { 13 | mulGeneric(a, b, c) 14 | } 15 | 16 | // Mul10 sets b to a*x. 17 | func Mul10(a, b *GF127) { 18 | mul10Generic(a, b) 19 | } 20 | 21 | // Mul11 sets b to a*(x+1). 22 | func Mul11(a, b *GF127) { 23 | mul11Generic(a, b) 24 | } 25 | -------------------------------------------------------------------------------- /gf127/gf127_test.go: -------------------------------------------------------------------------------- 1 | package gf127 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestAdd(t *testing.T) { 10 | var ( 11 | a = Random() 12 | b = Random() 13 | e = &GF127{a[0] ^ b[0], a[1] ^ b[1]} 14 | c = new(GF127) 15 | ) 16 | Add(a, b, c) 17 | require.Equal(t, e, c) 18 | } 19 | 20 | var testCasesMul = [][3]*GF127{ 21 | // (x+1)*(x^63+x^62+...+1) == x^64+1 22 | {&GF127{3, 0}, &GF127{maxUint64, 0}, &GF127{1, 1}}, 23 | 24 | // x^126 * x^2 == x^128 == x^64 + x 25 | {&GF127{0, 1 << 62}, &GF127{4, 0}, &GF127{2, 1}}, 26 | 27 | // (x^64+x^63+1) * (x^64+x) == x^128+x^65+x^127+x^64+x^64+x == x^65+x^64+x^63+1 28 | {&GF127{1 + 1<<63, 1}, &GF127{2, 1}, &GF127{0x8000000000000001, 3}}, 29 | } 30 | 31 | func TestMul(t *testing.T) { 32 | c := new(GF127) 33 | for _, tc := range testCasesMul { 34 | Mul(tc[0], tc[1], c) 35 | require.Equal(t, tc[2], c) 36 | } 37 | } 38 | 39 | func TestMulInPlace(t *testing.T) { 40 | for _, tc := range testCasesMul { 41 | a := *tc[0] 42 | b := *tc[1] 43 | Mul(&a, &b, &b) 44 | require.Equal(t, a, *tc[0]) 45 | require.Equal(t, b, *tc[2]) 46 | 47 | b = *tc[1] 48 | Mul(&a, &b, &a) 49 | require.Equal(t, b, *tc[1]) 50 | require.Equal(t, a, *tc[2]) 51 | } 52 | } 53 | 54 | var testCasesMul10 = [][2]*GF127{ 55 | {&GF127{123, 0}, &GF127{246, 0}}, 56 | {&GF127{maxUint64, 2}, &GF127{maxUint64 - 1, 5}}, 57 | {&GF127{0, maxUint64 >> 1}, &GF127{1 + 1<<63, maxUint64>>1 - 1}}, 58 | } 59 | 60 | func TestMul10(t *testing.T) { 61 | c := new(GF127) 62 | for _, tc := range testCasesMul10 { 63 | Mul10(tc[0], c) 64 | require.Equal(t, tc[1], c) 65 | } 66 | } 67 | 68 | var testCasesMul11 = [][2]*GF127{ 69 | {&GF127{123, 0}, &GF127{141, 0}}, 70 | {&GF127{maxUint64, 2}, &GF127{1, 7}}, 71 | {&GF127{0, maxUint64 >> 1}, &GF127{1 + 1<<63, 1}}, 72 | } 73 | 74 | func TestMul11(t *testing.T) { 75 | c := new(GF127) 76 | for _, tc := range testCasesMul11 { 77 | Mul11(tc[0], c) 78 | require.Equal(t, tc[1], c) 79 | } 80 | } 81 | 82 | var testCasesInv = [][2]*GF127{ 83 | {&GF127{1, 0}, &GF127{1, 0}}, 84 | {&GF127{3, 0}, &GF127{msb64, ^msb64}}, 85 | {&GF127{54321, 12345}, &GF127{8230555108620784737, 3929873967650665114}}, 86 | } 87 | 88 | func TestInv(t *testing.T) { 89 | var a, b, c = new(GF127), new(GF127), new(GF127) 90 | for _, tc := range testCasesInv { 91 | Inv(tc[0], c) 92 | require.Equal(t, tc[1], c) 93 | } 94 | 95 | for i := 0; i < 3; i++ { 96 | // 0 has no inverse 97 | if a = Random(); a.Equals(&GF127{0, 0}) { 98 | continue 99 | } 100 | Inv(a, b) 101 | Mul(a, b, c) 102 | require.Equal(t, &GF127{1, 0}, c) 103 | } 104 | } 105 | 106 | func TestGF127_MarshalBinary(t *testing.T) { 107 | a := New(0xFF, 0xEE) 108 | data, err := a.MarshalBinary() 109 | require.NoError(t, err) 110 | require.Equal(t, data, []byte{0, 0, 0, 0, 0, 0, 0, 0xEE, 0, 0, 0, 0, 0, 0, 0, 0xFF}) 111 | 112 | a = Random() 113 | data, err = a.MarshalBinary() 114 | require.NoError(t, err) 115 | 116 | b := new(GF127) 117 | err = b.UnmarshalBinary(data) 118 | require.NoError(t, err) 119 | require.Equal(t, a, b) 120 | 121 | err = b.UnmarshalBinary([]byte{0, 1, 2, 3}) 122 | require.Error(t, err) 123 | } 124 | -------------------------------------------------------------------------------- /gf127/gf127x2.go: -------------------------------------------------------------------------------- 1 | package gf127 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | ) 7 | 8 | // GF127x2 represents a pair of elements of GF(2^127) stored together. 9 | type GF127x2 [2]GF127 10 | 11 | func mul10x2Generic(a, b *GF127x2) { 12 | mul10Generic(&a[0], &b[0]) 13 | mul10Generic(&a[1], &b[1]) 14 | } 15 | 16 | func mul11x2Generic(a, b *GF127x2) { 17 | mul11Generic(&a[0], &b[0]) 18 | mul11Generic(&a[1], &b[1]) 19 | } 20 | 21 | // Split returns 2 components of pair without additional allocations. 22 | func Split(a *GF127x2) (*GF127, *GF127) { 23 | return &a[0], &a[1] 24 | } 25 | 26 | // CombineTo 2 elements of GF(2^127) to the respective components of pair. 27 | func CombineTo(a *GF127, b *GF127, c *GF127x2) { 28 | c[0] = *a 29 | c[1] = *b 30 | } 31 | 32 | // Equal checks if both elements of GF(2^127) pair are equal. 33 | func (a *GF127x2) Equal(b *GF127x2) bool { 34 | return a[0] == b[0] && a[1] == b[1] 35 | } 36 | 37 | // String returns hex-encoded representation, starting with MSB. 38 | // Elements of pair are separated by comma. 39 | func (a *GF127x2) String() string { 40 | b := a.Bytes() 41 | return hex.EncodeToString(b[:16]) + " , " + hex.EncodeToString(b[16:]) 42 | } 43 | 44 | // Bytes represents element of GF(2^127) as byte array of length 32. 45 | func (a *GF127x2) Bytes() (buf [32]byte) { 46 | binary.BigEndian.PutUint64(buf[:], a[0][1]) 47 | binary.BigEndian.PutUint64(buf[8:], a[0][0]) 48 | binary.BigEndian.PutUint64(buf[16:], a[1][1]) 49 | binary.BigEndian.PutUint64(buf[24:], a[1][0]) 50 | return 51 | } 52 | -------------------------------------------------------------------------------- /gf127/gf127x2_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build amd64 && !generic 2 | // +build amd64,!generic 3 | 4 | package gf127 5 | 6 | import "golang.org/x/sys/cpu" 7 | 8 | // Mul10x2 sets (b1, b2) to (a1*x, a2*x) 9 | func Mul10x2(a, b *GF127x2) { 10 | if cpu.X86.HasAVX && cpu.X86.HasAVX2 { 11 | mul10x2AVX2(a, b) 12 | } else { 13 | mul10x2Generic(a, b) 14 | } 15 | } 16 | 17 | // Mul11x2 sets (b1, b2) to (a1*(x+1), a2*(x+1)) 18 | func Mul11x2(a, b *GF127x2) { 19 | if cpu.X86.HasAVX && cpu.X86.HasAVX2 { 20 | mul11x2AVX2(a, b) 21 | } else { 22 | mul11x2Generic(a, b) 23 | } 24 | } 25 | 26 | func mul10x2AVX2(a, b *GF127x2) 27 | func mul11x2AVX2(a, b *GF127x2) 28 | -------------------------------------------------------------------------------- /gf127/gf127x2_amd64.s: -------------------------------------------------------------------------------- 1 | #include "textflag.h" 2 | 3 | // func Mul10x2(a, b) *[4]uint64 4 | TEXT ·mul10x2AVX2(SB), NOSPLIT, $0 5 | MOVQ a+0(FP), AX 6 | VMOVDQA (AX), Y0 7 | VPSLLQ $1, Y0, Y1 8 | VPALIGNR $8, Y1, Y0, Y2 9 | VPSRLQ $63, Y2, Y2 10 | VPXOR Y1, Y2, Y2 11 | VPSRLQ $63, Y1, Y3 12 | VPSLLQ $63, Y3, Y3 13 | VPUNPCKHQDQ Y3, Y3, Y3 14 | VPXOR Y2, Y3, Y3 15 | MOVQ b+8(FP), AX 16 | VMOVDQA Y3, (AX) 17 | RET 18 | 19 | // func Mul11x2(a, b) *[4]uint64 20 | TEXT ·mul11x2AVX2(SB), NOSPLIT, $0 21 | MOVQ a+0(FP), AX 22 | VMOVDQA (AX), Y0 23 | VPSLLQ $1, Y0, Y1 24 | VPALIGNR $8, Y1, Y0, Y2 25 | VPSRLQ $63, Y2, Y2 26 | VPXOR Y1, Y2, Y2 27 | VPSRLQ $63, Y1, Y3 28 | VPSLLQ $63, Y3, Y3 29 | VPUNPCKHQDQ Y3, Y3, Y3 30 | VPXOR Y2, Y3, Y3 31 | VPXOR Y0, Y3, Y3 32 | MOVQ b+8(FP), AX 33 | VMOVDQA Y3, (AX) 34 | RET 35 | -------------------------------------------------------------------------------- /gf127/gf127x2_generic.go: -------------------------------------------------------------------------------- 1 | //go:build !(amd64 && !generic) 2 | // +build !amd64 generic 3 | 4 | package gf127 5 | 6 | // Mul10x2 sets (b1, b2) to (a1*x, a2*x) 7 | func Mul10x2(a, b *GF127x2) { 8 | mul10x2Generic(a, b) 9 | } 10 | 11 | // Mul11x2 sets (b1, b2) to (a1*(x+1), a2*(x+1)) 12 | func Mul11x2(a, b *GF127x2) { 13 | mul11x2Generic(a, b) 14 | } 15 | -------------------------------------------------------------------------------- /gf127/gf127x2_test.go: -------------------------------------------------------------------------------- 1 | package gf127 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | var testCasesSplit = []struct { 10 | num *GF127x2 11 | h1 *GF127 12 | h2 *GF127 13 | }{ 14 | {&GF127x2{GF127{123, 31}, GF127{141, 9}}, &GF127{123, 31}, &GF127{141, 9}}, 15 | {&GF127x2{GF127{maxUint64, 0}, GF127{0, maxUint64}}, &GF127{maxUint64, 0}, &GF127{0, maxUint64}}, 16 | } 17 | 18 | func TestSplit(t *testing.T) { 19 | for _, tc := range testCasesSplit { 20 | a, b := Split(tc.num) 21 | require.Equal(t, tc.h1, a) 22 | require.Equal(t, tc.h2, b) 23 | } 24 | } 25 | 26 | func TestCombineTo(t *testing.T) { 27 | c := new(GF127x2) 28 | for _, tc := range testCasesSplit { 29 | CombineTo(tc.h1, tc.h2, c) 30 | require.Equal(t, tc.num, c) 31 | } 32 | } 33 | 34 | var testCasesMul10x2 = [][2]*GF127x2{ 35 | { 36 | &GF127x2{GF127{123, 0}, GF127{123, 0}}, 37 | &GF127x2{GF127{246, 0}, GF127{246, 0}}, 38 | }, 39 | { 40 | &GF127x2{GF127{maxUint64, 2}, GF127{0, 1}}, 41 | &GF127x2{GF127{maxUint64 - 1, 5}, GF127{0, 2}}, 42 | }, 43 | { 44 | &GF127x2{GF127{0, maxUint64 >> 1}, GF127{maxUint64, 2}}, 45 | &GF127x2{GF127{1 + 1<<63, maxUint64>>1 - 1}, GF127{maxUint64 - 1, 5}}, 46 | }, 47 | } 48 | 49 | func TestMul10x2(t *testing.T) { 50 | c := new(GF127x2) 51 | for _, tc := range testCasesMul10x2 { 52 | Mul10x2(tc[0], c) 53 | require.Equal(t, tc[1], c) 54 | } 55 | } 56 | 57 | var testCasesMul11x2 = [][2]*GF127x2{ 58 | { 59 | &GF127x2{GF127{123, 0}, GF127{123, 0}}, 60 | &GF127x2{GF127{141, 0}, GF127{141, 0}}, 61 | }, 62 | { 63 | &GF127x2{GF127{maxUint64, 2}, GF127{0, 1}}, 64 | &GF127x2{GF127{1, 7}, GF127{0, 3}}, 65 | }, 66 | { 67 | &GF127x2{GF127{0, maxUint64 >> 1}, GF127{maxUint64, 2}}, 68 | &GF127x2{GF127{1 + 1<<63, 1}, GF127{1, 7}}, 69 | }, 70 | } 71 | 72 | func TestMul11x2(t *testing.T) { 73 | c := new(GF127x2) 74 | for _, tc := range testCasesMul11x2 { 75 | Mul11x2(tc[0], c) 76 | require.Equal(t, tc[1], c) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/TrueCloudLab/tzhash 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 // indirect 7 | github.com/stretchr/testify v1.7.0 8 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 9 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 5 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 6 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 7 | github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= 8 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 9 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0= 10 | golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 11 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 12 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 13 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 14 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= 15 | gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 16 | -------------------------------------------------------------------------------- /help.mk: -------------------------------------------------------------------------------- 1 | .PHONY: help 2 | 3 | # Show this help prompt 4 | help: 5 | @echo ' Usage:' 6 | @echo '' 7 | @echo ' make ' 8 | @echo '' 9 | @echo ' Targets:' 10 | @echo '' 11 | @awk '/^#/{ comment = substr($$0,3) } comment && /^[a-zA-Z][a-zA-Z0-9_-]+ ?:/{ print " ", $$1, comment }' $(MAKEFILE_LIST) | column -t -s ':' | grep -v 'IGNORE' | sort | uniq 12 | -------------------------------------------------------------------------------- /tz/digest.go: -------------------------------------------------------------------------------- 1 | package tz 2 | 3 | import ( 4 | "github.com/TrueCloudLab/tzhash/gf127" 5 | ) 6 | 7 | const ( 8 | // Size is the size of a Tillich-Zémor hash sum in bytes. 9 | Size = 64 10 | hashBlockSize = 128 11 | ) 12 | 13 | type digest struct { 14 | // Stores matrix cells in the following order: 15 | // [ 0 2 ] 16 | // [ 1 3 ] 17 | // This is done to reuse the same digest between generic 18 | // and AVX2 implementation. 19 | x [4]GF127 20 | } 21 | 22 | // New returns a new hash.Hash computing the Tillich-Zémor checksum. 23 | func New() *digest { 24 | d := new(digest) 25 | d.Reset() 26 | return d 27 | } 28 | 29 | // Sum returns Tillich-Zémor checksum of data. 30 | func Sum(data []byte) [Size]byte { 31 | d := new(digest) 32 | d.Reset() 33 | _, _ = d.Write(data) // no errors 34 | return d.checkSum() 35 | } 36 | 37 | // Sum implements hash.Hash. 38 | func (d *digest) Sum(in []byte) []byte { 39 | // Make a copy of d so that caller can keep writing and summing. 40 | d0 := *d 41 | h := d0.checkSum() 42 | return append(in, h[:]...) 43 | } 44 | 45 | func (d *digest) checkSum() (b [Size]byte) { 46 | t := d.x[0].Bytes() 47 | copy(b[:], t[:]) 48 | 49 | t = d.x[2].Bytes() 50 | copy(b[16:], t[:]) 51 | 52 | t = d.x[1].Bytes() 53 | copy(b[32:], t[:]) 54 | 55 | t = d.x[3].Bytes() 56 | copy(b[48:], t[:]) 57 | 58 | return 59 | } 60 | 61 | // Reset implements hash.Hash. 62 | func (d *digest) Reset() { 63 | d.x[0] = GF127{1, 0} 64 | d.x[1] = GF127{0, 0} 65 | d.x[2] = GF127{0, 0} 66 | d.x[3] = GF127{1, 0} 67 | } 68 | 69 | // Write implements hash.Hash. 70 | func (d *digest) Write(data []byte) (n int, err error) { 71 | return write(d, data) 72 | } 73 | 74 | func writeGeneric(d *digest, data []byte) (n int, err error) { 75 | n = len(data) 76 | tmp := new(GF127) 77 | for _, b := range data { 78 | mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x80 != 0, tmp) 79 | mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x40 != 0, tmp) 80 | mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x20 != 0, tmp) 81 | mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x10 != 0, tmp) 82 | mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x08 != 0, tmp) 83 | mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x04 != 0, tmp) 84 | mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x02 != 0, tmp) 85 | mulBitRightGeneric(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b&0x01 != 0, tmp) 86 | } 87 | return 88 | } 89 | 90 | // Size implements hash.Hash. 91 | func (d *digest) Size() int { 92 | return Size 93 | } 94 | 95 | // BlockSize implements hash.Hash. 96 | func (d *digest) BlockSize() int { 97 | return hashBlockSize 98 | } 99 | 100 | func mulBitRightGeneric(c00, c10, c01, c11 *GF127, bit bool, tmp *GF127) { 101 | if bit { 102 | *tmp = *c00 103 | gf127.Mul10(c00, c00) 104 | gf127.Add(c00, c01, c00) 105 | gf127.Mul11(tmp, tmp) 106 | gf127.Add(c01, tmp, c01) 107 | 108 | *tmp = *c10 109 | gf127.Mul10(c10, c10) 110 | gf127.Add(c10, c11, c10) 111 | gf127.Mul11(tmp, tmp) 112 | gf127.Add(c11, tmp, c11) 113 | } else { 114 | *tmp = *c00 115 | gf127.Mul10(c00, c00) 116 | gf127.Add(c00, c01, c00) 117 | *c01 = *tmp 118 | 119 | *tmp = *c10 120 | gf127.Mul10(c10, c10) 121 | gf127.Add(c10, c11, c10) 122 | *c11 = *tmp 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /tz/digest_avx2_amd64.s: -------------------------------------------------------------------------------- 1 | #include "textflag.h" 2 | 3 | #define mulBit(bit, in_1, in_2, out_1, out_2) \ 4 | VPSLLW bit, Y10, Y11 \ 5 | VPSLLQ $1, in_1, Y1 \ 6 | VPSRAW $15, Y11, Y12 \ 7 | VPALIGNR $8, Y1, in_1, Y2 \ 8 | VPAND Y1, Y14, Y3 \ 9 | VPSRLQ $63, Y2, Y2 \ 10 | VPUNPCKHQDQ Y3, Y3, Y3 \ 11 | VPXOR Y1, Y2, Y7 \ 12 | VPXOR Y3, in_2, out_1 \ 13 | VPXOR Y7, out_1, out_1 \ 14 | VPAND out_1, Y12, Y4 \ 15 | VPXOR Y4, in_1, out_2 \ 16 | 17 | // func mulByteSliceRightx2(c00c10, c01c11 *[4]uint64, n int, data *byte) 18 | TEXT ·mulByteSliceRightx2(SB), NOSPLIT, $0 19 | MOVQ c00c10+0(FP), AX 20 | MOVQ c01c11+8(FP), BX 21 | 22 | VPXOR Y13, Y13, Y13 // Y13 = 0x0000... 23 | VPCMPEQB Y14, Y14, Y14 // Y14 = 0xFFFF... 24 | VPSUBQ Y14, Y13, Y10 25 | VPSLLQ $63, Y10, Y14 // Y14 = 0x10000000... (packed quad-words with HSB set) 26 | 27 | MOVQ n+16(FP), CX 28 | MOVQ data+24(FP), DX 29 | 30 | VMOVDQU (AX), Y0 31 | VMOVDQU (BX), Y8 32 | 33 | loop: 34 | CMPQ CX, $0 35 | JEQ finish 36 | 37 | VPBROADCASTB (DX), Y10 38 | ADDQ $1, DX 39 | SUBQ $1, CX 40 | 41 | mulBit($8, Y0, Y8, Y5, Y6) 42 | mulBit($9, Y5, Y6, Y0, Y8) 43 | mulBit($10, Y0, Y8, Y5, Y6) 44 | mulBit($11, Y5, Y6, Y0, Y8) 45 | mulBit($12, Y0, Y8, Y5, Y6) 46 | mulBit($13, Y5, Y6, Y0, Y8) 47 | mulBit($14, Y0, Y8, Y5, Y6) 48 | mulBit($15, Y5, Y6, Y0, Y8) 49 | 50 | JMP loop 51 | 52 | finish: 53 | VMOVDQU Y0, (AX) 54 | VMOVDQU Y8, (BX) 55 | 56 | RET 57 | -------------------------------------------------------------------------------- /tz/digest_avx_amd64.s: -------------------------------------------------------------------------------- 1 | #include "textflag.h" 2 | 3 | // mul2 multiplicates FROM by 2, stores result in R1 4 | // and uses R1, R2 and R3 for internal computations. 5 | #define mul2(FROM, TO, R2, R3) \ 6 | VPSLLQ $1, FROM, TO \ 7 | VPALIGNR $8, TO, FROM, R2 \ 8 | VPSRLQ $63, R2, R2 \ 9 | VANDPD TO, X14, R3 \ 10 | VPUNPCKHQDQ R3, R3, R3 \ 11 | VXORPD R2, TO, TO \ 12 | VXORPD R3, TO, TO 13 | 14 | #define mask(bit, tmp, to) \ 15 | VPSRLW bit, X10, tmp \ 16 | VPAND X12, tmp, to \ // to = 0x000000... 17 | VPSUBW to, X13, to // to = 0xFFFF.. or 0x0000 depending on bit 18 | 19 | #define mulBit(bit) \ 20 | VMOVDQU X0, X8 \ 21 | VMOVDQU X2, X9 \ 22 | mul2(X0, X5, X6, X7) \ 23 | VXORPD X1, X5, X0 \ 24 | mul2(X2, X5, X6, X7) \ 25 | VXORPD X3, X5, X2 \ 26 | mask(bit, X6, X5) \ 27 | VANDPD X0, X5, X1 \ 28 | VXORPD X8, X1, X1 \ 29 | VANDPD X2, X5, X3 \ 30 | VXORPD X9, X3, X3 31 | 32 | TEXT ·mulByteRight(SB), NOSPLIT, $0 33 | MOVQ c00+0(FP), AX 34 | VMOVDQU (AX), X0 35 | MOVQ c10+8(FP), CX 36 | VMOVDQU (CX), X2 37 | MOVQ c01+16(FP), BX 38 | VMOVDQU (BX), X1 39 | MOVQ c11+24(FP), DX 40 | VMOVDQU (DX), X3 41 | MOVQ $0, CX 42 | MOVB b+32(FP), CX 43 | 44 | VPXOR X13, X13, X13 // X13 = 0x0000... 45 | VPCMPEQB X14, X14, X14 // X14 = 0xFFFF... 46 | VPSUBQ X14, X13, X10 47 | VPSUBW X14, X13, X12 // X12 = 0x00010001... (packed words of 1) 48 | VPSLLQ $63, X10, X14 // X14 = 0x10000000... (packed quad-words with HSB set) 49 | 50 | MOVQ CX, X10 51 | VPSHUFLW $0, X10, X11 52 | VPSHUFD $0, X11, X10 53 | 54 | mulBit($7) 55 | mulBit($6) 56 | mulBit($5) 57 | mulBit($4) 58 | mulBit($3) 59 | mulBit($2) 60 | mulBit($1) 61 | mulBit($0) 62 | 63 | VMOVDQU X0, (AX) 64 | MOVQ c10+8(FP), CX 65 | VMOVDQU X2, (CX) 66 | VMOVDQU X1, (BX) 67 | MOVQ c11+24(FP), DX 68 | VMOVDQU X3, (DX) 69 | 70 | RET 71 | -------------------------------------------------------------------------------- /tz/digest_generic.go: -------------------------------------------------------------------------------- 1 | //go:build !(amd64 && !generic) 2 | // +build !amd64 generic 3 | 4 | package tz 5 | 6 | func write(d *digest, data []byte) (int, error) { 7 | return writeGeneric(d, data) 8 | } 9 | -------------------------------------------------------------------------------- /tz/digets_amd64.go: -------------------------------------------------------------------------------- 1 | //go:build amd64 && !generic 2 | // +build amd64,!generic 3 | 4 | package tz 5 | 6 | import ( 7 | "github.com/TrueCloudLab/tzhash/gf127" 8 | "golang.org/x/sys/cpu" 9 | ) 10 | 11 | func write(d *digest, data []byte) (n int, err error) { 12 | switch { 13 | case cpu.X86.HasAVX && cpu.X86.HasAVX2: 14 | return writeAVX2(d, data) 15 | case cpu.X86.HasAVX: 16 | return writeAVX(d, data) 17 | default: 18 | return writeGeneric(d, data) 19 | } 20 | } 21 | 22 | func writeAVX2(d *digest, data []byte) (n int, err error) { 23 | n = len(data) 24 | if len(data) != 0 { 25 | mulByteSliceRightx2(&d.x[0], &d.x[2], n, &data[0]) 26 | } 27 | return 28 | } 29 | 30 | func writeAVX(d *digest, data []byte) (n int, err error) { 31 | n = len(data) 32 | for _, b := range data { 33 | mulByteRight(&d.x[0], &d.x[1], &d.x[2], &d.x[3], b) 34 | } 35 | return 36 | } 37 | 38 | func mulByteRight(c00, c01, c10, c11 *GF127, b byte) 39 | func mulByteSliceRightx2(c00c10 *gf127.GF127, c01c11 *gf127.GF127, n int, data *byte) 40 | -------------------------------------------------------------------------------- /tz/hash.go: -------------------------------------------------------------------------------- 1 | // Package tz contains Tillich-Zemor checksum implementations 2 | // using different backends. 3 | // 4 | // Copyright 2022 (c) NSPCC 5 | package tz 6 | 7 | import ( 8 | "errors" 9 | ) 10 | 11 | // Concat performs combining of hashes based on homomorphic property. 12 | func Concat(hs [][]byte) ([]byte, error) { 13 | var b, c sl2 14 | 15 | b = id 16 | for i := range hs { 17 | if err := c.UnmarshalBinary(hs[i]); err != nil { 18 | return nil, err 19 | } 20 | b.Mul(&b, &c) 21 | } 22 | return b.MarshalBinary() 23 | } 24 | 25 | // Validate checks if hashes in hs combined are equal to h. 26 | func Validate(h []byte, hs [][]byte) (bool, error) { 27 | var ( 28 | b []byte 29 | got, expected [Size]byte 30 | err error 31 | ) 32 | 33 | if len(h) != Size { 34 | return false, errors.New("invalid hash") 35 | } else if len(hs) == 0 { 36 | return false, errors.New("empty slice") 37 | } 38 | 39 | copy(expected[:], h) 40 | 41 | b, err = Concat(hs) 42 | if err != nil { 43 | return false, errors.New("cant concatenate hashes") 44 | } 45 | 46 | copy(got[:], b) 47 | 48 | return expected == got, nil 49 | } 50 | 51 | // SubtractR returns hash a, such that Concat(a, b) == c 52 | // This is possible, because Tillich-Zemor hash is actually a matrix 53 | // which can be inversed. 54 | func SubtractR(c, b []byte) (a []byte, err error) { 55 | var p1, p2, r sl2 56 | 57 | if err = r.UnmarshalBinary(c); err != nil { 58 | return nil, err 59 | } 60 | if err = p2.UnmarshalBinary(b); err != nil { 61 | return nil, err 62 | } 63 | 64 | p1 = *Inv(&p2) 65 | p1.Mul(&r, &p1) 66 | 67 | return p1.MarshalBinary() 68 | } 69 | 70 | // SubtractL returns hash b, such that Concat(a, b) == c 71 | // This is possible, because Tillich-Zemor hash is actually a matrix 72 | // which can be inversed. 73 | func SubtractL(c, a []byte) (b []byte, err error) { 74 | var p1, p2, r sl2 75 | 76 | if err = r.UnmarshalBinary(c); err != nil { 77 | return nil, err 78 | } 79 | if err = p1.UnmarshalBinary(a); err != nil { 80 | return nil, err 81 | } 82 | 83 | p2 = *Inv(&p1) 84 | p2.Mul(&p2, &r) 85 | 86 | return p2.MarshalBinary() 87 | } 88 | -------------------------------------------------------------------------------- /tz/hash_test.go: -------------------------------------------------------------------------------- 1 | package tz 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "io" 7 | "math/rand" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/require" 11 | "golang.org/x/sys/cpu" 12 | ) 13 | 14 | const benchDataSize = 100000 15 | 16 | type arch struct { 17 | HasAVX bool 18 | HasAVX2 bool 19 | } 20 | 21 | var backends = []struct { 22 | Name string 23 | arch 24 | }{ 25 | {"AVX", arch{true, false}}, 26 | {"AVX2", arch{true, true}}, 27 | {"Generic", arch{false, false}}, 28 | } 29 | 30 | var testCases = []struct { 31 | input []byte 32 | hash string 33 | }{ 34 | { 35 | []byte{}, 36 | "00000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", 37 | }, 38 | { 39 | []byte{0}, 40 | "00000000000000000000000000000151000000000000000000000000000000800000000000000000000000000000008000000000000000000000000000000051", 41 | }, 42 | { 43 | []byte{1, 2}, 44 | "000000000000000000000000000139800000000000000000000000000000c0010000000000000000000000000000b98100000000000000000000000000007981", 45 | }, 46 | { 47 | []byte{2, 0, 1}, 48 | "00000000000000000000000001f980d10000000000000000000000000139805100000000000000000000000000c001d100000000000000000000000000b98080", 49 | }, 50 | { 51 | []byte{3, 2, 1, 0}, 52 | "0000000000000000000000015540398000000000000000000000000082a1a88100000000000000000000000082a1d10100000000000000000000000050006881", 53 | }, 54 | { 55 | []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, 56 | "0000000000000000000001bb00ba00ba000000000000000000000101010101010000000000000000000000ff00ff00ff0000000000000000000000ba01bb01bb", 57 | }, 58 | { 59 | []byte{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA}, 60 | "000000000000000000016ad06ad16bd100000000000000000000ff00ff00ff0000000000000000000000808080808080000000000000000000006bd16bd06ad1", 61 | }, 62 | { 63 | []byte{0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55}, 64 | "0000000000000000018c8c118d9d009d00000000000000000169680169680168000000000000000000f0f000f0f000f00000000000000000009d9c109c8d018d", 65 | }, 66 | { 67 | []byte{0, 1, 2, 3, 4, 5, 6, 7, 8}, 68 | "00000000000001e4a545e5b90fb6882b00000000000000c849cd88f79307f67100000000000000cd0c898cb68356e624000000000000007cbcdc7c5e89b16e4b", 69 | }, 70 | { 71 | []byte{4, 8, 15, 16, 23, 42, 255, 0, 127, 65, 32, 123, 42, 45, 201, 210, 213, 244}, 72 | "4db8a8e253903c70ab0efb65fe6de05a36d1dc9f567a147152d0148a86817b2062908d9b026a506007c1118e86901b672a39317c55ee3c10ac8efafa79efe8ee", 73 | }, 74 | } 75 | 76 | func TestHash(t *testing.T) { 77 | for i, b := range backends { 78 | t.Run(b.Name+" digest", func(t *testing.T) { 79 | prepareArch(t, backends[i].arch) 80 | 81 | fmt.Println("FEATURES:", cpu.X86.HasAVX, cpu.X86.HasAVX2) 82 | d := New() 83 | for _, tc := range testCases { 84 | d.Reset() 85 | _, _ = d.Write(tc.input) 86 | sum := d.Sum(nil) 87 | require.Equal(t, tc.hash, hex.EncodeToString(sum[:])) 88 | } 89 | }) 90 | } 91 | } 92 | 93 | func prepareArch(t testing.TB, b arch) { 94 | realCPU := cpu.X86 95 | if !realCPU.HasAVX2 && b.HasAVX2 || !realCPU.HasAVX && b.HasAVX { 96 | t.Skip("Underlying CPU doesn't support necessary features") 97 | } else { 98 | t.Cleanup(func() { 99 | cpu.X86.HasAVX = realCPU.HasAVX 100 | cpu.X86.HasAVX2 = realCPU.HasAVX2 101 | }) 102 | cpu.X86.HasAVX = b.HasAVX 103 | cpu.X86.HasAVX2 = b.HasAVX2 104 | } 105 | } 106 | 107 | func newBuffer() (data []byte) { 108 | data = make([]byte, benchDataSize) 109 | 110 | r := rand.New(rand.NewSource(0)) 111 | _, err := io.ReadFull(r, data) 112 | if err != nil { 113 | panic("cant initialize buffer") 114 | } 115 | return 116 | } 117 | 118 | func BenchmarkSum(b *testing.B) { 119 | data := newBuffer() 120 | 121 | for i := range backends { 122 | b.Run(backends[i].Name+" digest", func(b *testing.B) { 123 | prepareArch(b, backends[i].arch) 124 | 125 | b.ResetTimer() 126 | b.ReportAllocs() 127 | d := New() 128 | for i := 0; i < b.N; i++ { 129 | d.Reset() 130 | _, _ = d.Write(data) 131 | d.Sum(nil) 132 | } 133 | b.SetBytes(int64(len(data))) 134 | }) 135 | } 136 | } 137 | 138 | func TestHomomorphism(t *testing.T) { 139 | var ( 140 | c1, c2 sl2 141 | n int 142 | err error 143 | h, h1, h2 [Size]byte 144 | b []byte 145 | ) 146 | 147 | b = make([]byte, 64) 148 | n, err = rand.Read(b) 149 | require.Equal(t, 64, n) 150 | require.NoError(t, err) 151 | 152 | // Test if our hashing is really homomorphic 153 | h = Sum(b) 154 | require.NotEqual(t, [64]byte{}, h) 155 | h1 = Sum(b[:32]) 156 | h2 = Sum(b[32:]) 157 | 158 | err = c1.UnmarshalBinary(h1[:]) 159 | require.NoError(t, err) 160 | err = c2.UnmarshalBinary(h2[:]) 161 | require.NoError(t, err) 162 | 163 | c1.Mul(&c1, &c2) 164 | require.Equal(t, h, c1.Bytes()) 165 | } 166 | 167 | var testCasesConcat = []struct { 168 | Hash string 169 | Parts []string 170 | }{{ 171 | Hash: "7f5c9280352a8debea738a74abd4ec787f2c5e556800525692f651087442f9883bb97a2c1bc72d12ba26e3df8dc0f670564292ebc984976a8e353ff69a5fb3cb", 172 | Parts: []string{ 173 | "4275945919296224acd268456be23b8b2df931787a46716477e32cd991e98074029d4f03a0fedc09125ee4640d228d7d40d430659a0b2b70e9cd4d4c5361865a", 174 | "2828661d1b1e77f21788d3b365f140a2395d57dc2083c33e60d9a80e69017d5016a249c7adfe1718a10ba887dedbdaec5c4c1fbecdb1f98776b43f1142c26a88", 175 | "02310598b45dfa77db9f00eed6ab60773dd8bed7bdac431b42e441fae463f64c6e2688402cfdcec5def47a299b0651fb20878cf4410991bd57056d7b4b31635a", 176 | "1ed7e0b065c060d915e7355cdcb4edc752c06d2a4b39d90c8985aeb58e08cb9e5bbe4b2b45524efbd68cd7e4081a1b8362941200a4c9f76a0a9f9ac9b7868c03", 177 | "6f11e3dc4fff99ffa45e36e4655cfc657c29e950e598a90f426bf5710de9171323523db7636643b23892783f4fb3cf8e583d584c82d29558a105a615a668fc9e", 178 | "1865dbdb4c849620fb2c4809d75d62490f83c11f2145abaabbdc9a66ae58ce1f2e42c34d3b380e5dea1b45217750b42d130f995b162afbd2e412b0d41ec8871b", 179 | "5102dd1bd1f08f44dbf3f27ac895020d63f96044ce3b491aed3efbc7bbe363bc5d800101d63890f89a532427812c30c9674f37476ba44daf758afa88d4f91063", 180 | "70cab735dad90164cc61f7411396221c4e549f12392c0d77728c89a9754f606c7d961169d4fa88133a1ba954bad616656c86f8fd1335a2f3428fd4dca3a3f5a5", 181 | "430f3e92536ff9a50cbcdf08d8810a59786ca37e31d54293646117a93469f61c6cdd67933128407d77f3235293293ee86dbc759d12dfe470969eba1b4a373bd0", 182 | "46e1d97912ca2cf92e6a9a63667676835d900cdb2fff062136a64d8d60a8e5aa644ccee3558900af8e77d56b013ed5da12d9d0b7de0f56976e040b3d01345c0d", 183 | }, 184 | }} 185 | 186 | func TestConcat(t *testing.T) { 187 | var ( 188 | actual, expect []byte 189 | ps [][]byte 190 | err error 191 | ) 192 | 193 | for _, tc := range testCasesConcat { 194 | expect, err = hex.DecodeString(tc.Hash) 195 | require.NoError(t, err) 196 | 197 | ps = make([][]byte, len(tc.Parts)) 198 | for j := 0; j < len(tc.Parts); j++ { 199 | ps[j], err = hex.DecodeString(tc.Parts[j]) 200 | require.NoError(t, err) 201 | } 202 | 203 | actual, err = Concat(ps) 204 | require.NoError(t, err) 205 | require.Equal(t, expect, actual) 206 | } 207 | } 208 | 209 | func TestValidate(t *testing.T) { 210 | var ( 211 | h []byte 212 | ps [][]byte 213 | got bool 214 | err error 215 | ) 216 | 217 | for _, tc := range testCasesConcat { 218 | h, _ = hex.DecodeString(tc.Hash) 219 | require.NoError(t, err) 220 | 221 | ps = make([][]byte, len(tc.Parts)) 222 | for j := 0; j < len(tc.Parts); j++ { 223 | ps[j], _ = hex.DecodeString(tc.Parts[j]) 224 | require.NoError(t, err) 225 | } 226 | 227 | got, err = Validate(h, ps) 228 | require.NoError(t, err) 229 | require.True(t, got) 230 | } 231 | } 232 | 233 | var testCasesSubtract = []struct { 234 | first, second, result string 235 | }{ 236 | { 237 | first: "4275945919296224acd268456be23b8b2df931787a46716477e32cd991e98074029d4f03a0fedc09125ee4640d228d7d40d430659a0b2b70e9cd4d4c5361865a", 238 | second: "277c10e0d7c52fcc0b23ba7dbf2c3dde7dcfc1f7c0cc0d998b2de504b8c1e17c6f65ab1294aea676d4060ed2ca18c1c26fd7cec5012ab69a4ddb5e6555ac8a59", 239 | result: "7f5c9280352a8debea738a74abd4ec787f2c5e556800525692f651087442f9883bb97a2c1bc72d12ba26e3df8dc0f670564292ebc984976a8e353ff69a5fb3cb", 240 | }, 241 | { 242 | first: "18e2ce290cc74998ebd0bef76454b52a40428f13bb612e40b5b96187e9cc813248a0ed5f7ec9fb205d55d3f243e2211363f171b19eb8acc7931cf33853a79069", 243 | second: "73a0582fa7d00d62fd09c1cd18589cdb2b126cb58b3a022ae47a8a787dabe35c4388aaf0d8bb343b1e58ee8d267812d115f40a0da611f42458f452e102f60700", 244 | result: "54ccaad1bb15b2989fa31109713bca955ea5d87bbd3113b3008cea167c00052266e9c9fcb73ece98c6c08cccb074ba3d39b5d8685f022fc388e2bf1997c5bd1d", 245 | }, 246 | } 247 | 248 | func TestSubtract(t *testing.T) { 249 | var ( 250 | a, b, c, r []byte 251 | err error 252 | ) 253 | 254 | for _, tc := range testCasesSubtract { 255 | a, err = hex.DecodeString(tc.first) 256 | require.NoError(t, err) 257 | 258 | b, err = hex.DecodeString(tc.second) 259 | require.NoError(t, err) 260 | 261 | c, err = hex.DecodeString(tc.result) 262 | require.NoError(t, err) 263 | 264 | r, err = SubtractR(c, b) 265 | require.NoError(t, err) 266 | require.Equal(t, a, r) 267 | 268 | r, err = SubtractL(c, a) 269 | require.NoError(t, err) 270 | require.Equal(t, b, r) 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /tz/sl2.go: -------------------------------------------------------------------------------- 1 | package tz 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/TrueCloudLab/tzhash/gf127" 7 | ) 8 | 9 | type ( 10 | GF127 = gf127.GF127 11 | 12 | sl2 [2][2]GF127 13 | ) 14 | 15 | var id = sl2{ 16 | {GF127{1, 0}, GF127{0, 0}}, 17 | {GF127{0, 0}, GF127{1, 0}}, 18 | } 19 | 20 | // MarshalBinary implements encoding.BinaryMarshaler. 21 | func (c *sl2) MarshalBinary() (data []byte, err error) { 22 | s := c.Bytes() 23 | return s[:], nil 24 | } 25 | 26 | // UnmarshalBinary implements encoding.BinaryUnmarshaler. 27 | func (c *sl2) UnmarshalBinary(data []byte) (err error) { 28 | if len(data) != 64 { 29 | return errors.New("data must be 64-bytes long") 30 | } 31 | 32 | if err = c[0][0].UnmarshalBinary(data[:16]); err != nil { 33 | return 34 | } 35 | if err = c[0][1].UnmarshalBinary(data[16:32]); err != nil { 36 | return 37 | } 38 | if err = c[1][0].UnmarshalBinary(data[32:48]); err != nil { 39 | return 40 | } 41 | if err = c[1][1].UnmarshalBinary(data[48:64]); err != nil { 42 | return 43 | } 44 | 45 | return 46 | } 47 | 48 | func (c *sl2) mulStrassen(a, b *sl2, x *[8]GF127) *sl2 { //nolint:unused 49 | // strassen algorithm 50 | gf127.Add(&a[0][0], &a[1][1], &x[0]) 51 | gf127.Add(&b[0][0], &b[1][1], &x[1]) 52 | gf127.Mul(&x[0], &x[1], &x[0]) 53 | 54 | gf127.Add(&a[1][0], &a[1][1], &x[1]) 55 | gf127.Mul(&x[1], &b[0][0], &x[1]) 56 | 57 | gf127.Add(&b[0][1], &b[1][1], &x[2]) 58 | gf127.Mul(&x[2], &a[0][0], &x[2]) 59 | 60 | gf127.Add(&b[1][0], &b[0][0], &x[3]) 61 | gf127.Mul(&x[3], &a[1][1], &x[3]) 62 | 63 | gf127.Add(&a[0][0], &a[0][1], &x[4]) 64 | gf127.Mul(&x[4], &b[1][1], &x[4]) 65 | 66 | gf127.Add(&a[1][0], &a[0][0], &x[5]) 67 | gf127.Add(&b[0][0], &b[0][1], &x[6]) 68 | gf127.Mul(&x[5], &x[6], &x[5]) 69 | 70 | gf127.Add(&a[0][1], &a[1][1], &x[6]) 71 | gf127.Add(&b[1][0], &b[1][1], &x[7]) 72 | gf127.Mul(&x[6], &x[7], &x[6]) 73 | 74 | gf127.Add(&x[2], &x[4], &c[0][1]) 75 | gf127.Add(&x[1], &x[3], &c[1][0]) 76 | 77 | gf127.Add(&x[4], &x[6], &x[4]) 78 | gf127.Add(&x[0], &x[3], &c[0][0]) 79 | gf127.Add(&c[0][0], &x[4], &c[0][0]) 80 | 81 | gf127.Add(&x[0], &x[1], &x[0]) 82 | gf127.Add(&x[2], &x[5], &c[1][1]) 83 | gf127.Add(&c[1][1], &x[0], &c[1][1]) 84 | 85 | return c 86 | } 87 | 88 | func (c *sl2) MulA() *sl2 { 89 | var a GF127 90 | 91 | gf127.Mul10(&c[0][0], &a) 92 | gf127.Mul1(&c[0][0], &c[0][1]) 93 | gf127.Add(&a, &c[0][1], &c[0][0]) 94 | 95 | gf127.Mul10(&c[1][0], &a) 96 | gf127.Mul1(&c[1][0], &c[1][1]) 97 | gf127.Add(&a, &c[1][1], &c[1][0]) 98 | 99 | return c 100 | } 101 | 102 | func (c *sl2) MulB() *sl2 { 103 | var a GF127 104 | 105 | gf127.Mul1(&c[0][0], &a) 106 | gf127.Mul10(&c[0][0], &c[0][0]) 107 | gf127.Add(&c[0][1], &c[0][0], &c[0][0]) 108 | gf127.Add(&c[0][0], &a, &c[0][1]) 109 | 110 | gf127.Mul1(&c[1][0], &a) 111 | gf127.Mul10(&c[1][0], &c[1][0]) 112 | gf127.Add(&c[1][1], &c[1][0], &c[1][0]) 113 | gf127.Add(&c[1][0], &a, &c[1][1]) 114 | 115 | return c 116 | } 117 | 118 | // Mul returns a * b in GL_2(GF(2^127)) 119 | func (c *sl2) Mul(a, b *sl2) *sl2 { 120 | var x [4]GF127 121 | 122 | gf127.Mul(&a[0][0], &b[0][0], &x[0]) 123 | gf127.Mul(&a[0][0], &b[0][1], &x[1]) 124 | gf127.Mul(&a[1][0], &b[0][0], &x[2]) 125 | gf127.Mul(&a[1][0], &b[0][1], &x[3]) 126 | 127 | gf127.Mul(&a[0][1], &b[1][0], &c[0][0]) 128 | gf127.Add(&c[0][0], &x[0], &c[0][0]) 129 | gf127.Mul(&a[0][1], &b[1][1], &c[0][1]) 130 | gf127.Add(&c[0][1], &x[1], &c[0][1]) 131 | gf127.Mul(&a[1][1], &b[1][0], &c[1][0]) 132 | gf127.Add(&c[1][0], &x[2], &c[1][0]) 133 | gf127.Mul(&a[1][1], &b[1][1], &c[1][1]) 134 | gf127.Add(&c[1][1], &x[3], &c[1][1]) 135 | return c 136 | } 137 | 138 | // Inv returns inverse of a in GL_2(GF(2^127)) 139 | func Inv(a *sl2) (b *sl2) { 140 | b = new(sl2) 141 | inv(a, b, new([2]GF127)) 142 | return 143 | } 144 | 145 | func inv(a, b *sl2, t *[2]GF127) { 146 | gf127.Mul(&a[0][0], &a[1][1], &t[0]) 147 | gf127.Mul(&a[0][1], &a[1][0], &t[1]) 148 | gf127.Add(&t[0], &t[1], &t[0]) 149 | gf127.Inv(&t[0], &t[1]) 150 | 151 | gf127.Mul(&t[1], &a[0][0], &b[1][1]) 152 | gf127.Mul(&t[1], &a[0][1], &b[0][1]) 153 | gf127.Mul(&t[1], &a[1][0], &b[1][0]) 154 | gf127.Mul(&t[1], &a[1][1], &b[0][0]) 155 | } 156 | 157 | func (c *sl2) String() string { 158 | return c[0][0].String() + c[0][1].String() + 159 | c[1][0].String() + c[1][1].String() 160 | } 161 | 162 | func (c *sl2) Bytes() (b [Size]byte) { 163 | t := c[0][0].Bytes() 164 | copy(b[:], t[:]) 165 | 166 | t = c[0][1].Bytes() 167 | copy(b[16:], t[:]) 168 | 169 | t = c[1][0].Bytes() 170 | copy(b[32:], t[:]) 171 | 172 | t = c[1][1].Bytes() 173 | copy(b[48:], t[:]) 174 | 175 | return 176 | } 177 | -------------------------------------------------------------------------------- /tz/sl2_test.go: -------------------------------------------------------------------------------- 1 | package tz 2 | 3 | import ( 4 | "math/rand" 5 | "testing" 6 | "time" 7 | 8 | "github.com/TrueCloudLab/tzhash/gf127" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func init() { 13 | rand.Seed(time.Now().UnixNano()) 14 | } 15 | 16 | func random() (a *sl2) { 17 | a = new(sl2) 18 | a[0][0] = *gf127.Random() 19 | a[0][1] = *gf127.Random() 20 | a[1][0] = *gf127.Random() 21 | 22 | // so that result is in SL2 23 | // d = a^-1*(1+b*c) 24 | gf127.Mul(&a[0][1], &a[1][0], &a[1][1]) 25 | gf127.Add(&a[1][1], gf127.New(1, 0), &a[1][1]) 26 | 27 | t := gf127.New(0, 0) 28 | gf127.Inv(&a[0][0], t) 29 | gf127.Mul(t, &a[1][1], &a[1][1]) 30 | 31 | return 32 | } 33 | 34 | func TestSL2_MarshalBinary(t *testing.T) { 35 | var ( 36 | a = random() 37 | b = new(sl2) 38 | ) 39 | 40 | data, err := a.MarshalBinary() 41 | require.NoError(t, err) 42 | 43 | err = b.UnmarshalBinary(data) 44 | require.NoError(t, err) 45 | 46 | require.Equal(t, a, b) 47 | } 48 | 49 | func TestInv(t *testing.T) { 50 | var a, b, c *sl2 51 | 52 | c = new(sl2) 53 | for i := 0; i < 5; i++ { 54 | a = random() 55 | b = Inv(a) 56 | c = c.Mul(a, b) 57 | 58 | require.Equal(t, id, *c) 59 | } 60 | } 61 | --------------------------------------------------------------------------------