├── .github └── workflows │ ├── codeql-analysis.yml │ └── go.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── GNUmakefile ├── LICENSE ├── README.md ├── apps ├── .gitignore ├── circuit │ ├── GNUmakefile │ ├── and.circ │ ├── main.go │ └── not.circ ├── garbled │ ├── GNUmakefile │ ├── bmr.go │ ├── compile.go │ ├── default.pgo │ ├── examples │ │ ├── .gitignore │ │ ├── 3party.mpcl │ │ ├── add.mpcl │ │ ├── aesblock.mpcl │ │ ├── aesblock2.mpcl │ │ ├── aescbc.mpcl │ │ ├── aesexpand.mpcl │ │ ├── aesgcm.mpcl │ │ ├── and.mpcl │ │ ├── credit.mpcl │ │ ├── div.mpcl │ │ ├── ecdh │ │ │ └── keygen.mpcl │ │ ├── ed25519 │ │ │ ├── keygen.mpcl │ │ │ └── sign.mpcl │ │ ├── encrypt.mpcl │ │ ├── hamming.mpcl │ │ ├── hmac-sha256.mpcl │ │ ├── hmac-sha512.mpcl │ │ ├── key-import.mpcl │ │ ├── millionaire.mpcl │ │ ├── montgomery.mpcl │ │ ├── mult.mpcl │ │ ├── mult1024.mpcl │ │ ├── rps.mpcl │ │ ├── rsa.mpcl │ │ ├── rsasign.mpcl │ │ └── sort.mpcl │ ├── main.go │ └── streaming.go ├── iotest │ ├── iotest.go │ └── main.go ├── iter │ └── main.go ├── mpcldoc │ ├── doc.go │ ├── doc_html.go │ └── main.go ├── objdump │ ├── main.go │ └── objdump.go └── ot │ └── main.go ├── benchmarks.md ├── bmr ├── 3party_test.go ├── README.md ├── fx.go ├── fx_test.go ├── network.go ├── operand_string.go ├── peer.go ├── player.go ├── testdata │ ├── 3party.circ.svg │ ├── 3party.mpcl │ └── 3party.mpclc └── wire.go ├── circuit ├── aesni │ ├── .gitignore │ ├── GNUmakefile │ ├── c │ │ └── aesni.c │ └── go_test.go ├── analyze.go ├── circuit.go ├── circuit_test.go ├── computer.go ├── docs │ ├── and.svg │ ├── not.svg │ ├── or.svg │ ├── wire.svg │ ├── xnor.svg │ └── xor.svg ├── dot.go ├── enc_test.go ├── eval.go ├── evaluator.go ├── garble.go ├── garbler.go ├── ioarg.go ├── ioarg_test.go ├── marshal.go ├── parser.go ├── parser_test.go ├── player.go ├── stream_evaluator.go ├── stream_garble.go ├── stream_garble_test.go ├── svg.go ├── template.go ├── template_test.go └── timing.go ├── compiler ├── .gitignore ├── README.md ├── arithmetic_test.go ├── ast │ ├── ast.go │ ├── builtin.go │ ├── codegen.go │ ├── eval.go │ ├── lrvalue.go │ ├── package.go │ └── ssagen.go ├── circuits │ ├── allocator.go │ ├── circ_adder.go │ ├── circ_binary.go │ ├── circ_comparators.go │ ├── circ_divider.go │ ├── circ_hamming.go │ ├── circ_index.go │ ├── circ_multiplier.go │ ├── circ_multiplier_params.go │ ├── circ_mux.go │ ├── circ_subtractor.go │ ├── circuits_test.go │ ├── compiler.go │ ├── gates.go │ ├── wire.go │ └── wire_test.go ├── compiler.go ├── compiler_test.go ├── lexer.go ├── lexer_test.go ├── mpa │ ├── doc.go │ ├── mpint.go │ ├── mpint128_test.go │ ├── mpint32_test.go │ └── mpint64_test.go ├── parser.go ├── parser_test.go ├── ssa │ ├── bindings.go │ ├── bindings_test.go │ ├── block.go │ ├── circuitgen.go │ ├── generator.go │ ├── instructions.go │ ├── opcodes_test.go │ ├── peephole.go │ ├── program.go │ ├── set.go │ ├── streamer.go │ ├── value.go │ ├── value_test.go │ └── wire_allocator.go ├── ssagen_test.go └── utils │ ├── logger.go │ ├── params.go │ ├── point.go │ └── point_test.go ├── docs ├── apidoc │ ├── index,content.md │ ├── index.toml │ ├── language,content.md │ ├── language.toml │ └── mpcl.html ├── ifelse.png ├── max.png ├── mpcc.png ├── mpcl-compiler.ditaa ├── mpcl-compiler.png ├── mpcl.html ├── mpcl.iso-ebnf ├── mpcl.svg └── ref │ ├── ecdh │ ├── GNUmakefile │ ├── curve25519 │ │ ├── curve25519.go │ │ └── doc.go │ └── main.go │ ├── ed25519 │ ├── GNUmakefile │ ├── edwards25519 │ │ ├── const.go │ │ └── edwards25519.go │ └── main.go │ └── sha1 │ ├── GNUmakefile │ ├── crypto │ └── sha1 │ │ ├── block.go │ │ ├── sha1.go │ │ └── sha1go.go │ └── main.go ├── go.mod ├── go.sum ├── mult64.iql ├── ot ├── README.md ├── co.go ├── co_test.go ├── io.go ├── label.go ├── label_test.go ├── mpint │ ├── mpint.go │ └── mpint_test.go ├── ot.go ├── ot_test.go ├── pipe.go ├── pipe_test.go ├── rsa.go └── rsa_test.go ├── p2p ├── network.go ├── protocol.go └── protocol_test.go ├── pkg ├── README.md ├── builtin.mpcl ├── bytes │ ├── bytes.mpcl │ └── doc.mpcl ├── crypto │ ├── aes │ │ ├── aes_128.circ │ │ ├── aes_192.circ │ │ ├── aes_256.circ │ │ ├── block.mpcl │ │ ├── cipher.mpcl │ │ ├── circuit.mpcl │ │ └── const.mpcl │ ├── cipher │ │ ├── cbc │ │ │ └── cbc.mpcl │ │ ├── cts │ │ │ └── cts.mpcl │ │ └── gcm │ │ │ └── gcm.mpcl │ ├── curve25519 │ │ ├── curve25519.mpcl │ │ └── doc.mpcl │ ├── ed25519 │ │ ├── README.md │ │ ├── internal │ │ │ └── edwards25519 │ │ │ │ ├── const.mpcl │ │ │ │ └── ed25519.mpcl │ │ ├── keygen.mpcl │ │ └── sign.mpcl │ ├── hmac │ │ ├── doc.mpcl │ │ ├── sha1.mpcl │ │ ├── sha256.mpcl │ │ └── sha512.mpcl │ ├── rsa │ │ └── rsa.mpcl │ ├── sha1 │ │ ├── block.mpcl │ │ └── sha1.mpcl │ ├── sha256 │ │ ├── sha256.circ │ │ └── sum.mpcl │ └── sha512 │ │ ├── sha512.circ │ │ ├── sha512.mpclc │ │ └── sum.mpcl ├── encoding │ ├── binary │ │ ├── doc.mpcl │ │ ├── getput.mpcl │ │ └── metrics.mpcl │ └── hex │ │ ├── doc.mpcl │ │ └── hex.mpcl ├── math │ ├── add64.circ │ ├── bits │ │ └── bits.mpcl │ ├── const.mpcl │ ├── div64.circ │ ├── doc.mpcl │ ├── integer.mpcl │ ├── modp.mpcl │ ├── montgomery.mpcl │ ├── mul64.circ │ └── sub64.circ └── sort │ └── sort.mpcl ├── result.go ├── rsa32.csv ├── rsa32.iql ├── testsuite ├── crypto │ ├── cipher │ │ ├── cts │ │ │ ├── aes128_cts_dec.mpcl │ │ │ └── aes128_cts_enc.mpcl │ │ └── gcm │ │ │ └── aes128_gcm.mpcl │ ├── hmac_sha1.mpcl │ ├── hmac_sha256.mpcl │ ├── rsa.mpcl │ ├── sha1.mpcl │ ├── sha256_block.mpcl │ ├── sha256_block_block.mpcl │ ├── sha256_block_pad.mpcl │ ├── sha512_block.mpcl │ ├── sha512_block_block.mpcl │ ├── sha512_block_pad.mpcl │ ├── sha512_test_.mpcl │ └── sha512_test_abc.mpcl ├── lang │ ├── array.mpcl │ ├── assign2.mpcl │ ├── cast_bytearr_string.mpcl │ ├── cast_string_bytearr.mpcl │ ├── cast_string_int.mpcl │ ├── composite_lit.mpcl │ ├── const_cast_bytearr_string.mpcl │ ├── const_cast_int_0.mpcl │ ├── const_cast_int_1.mpcl │ ├── const_cast_string_bytearr.mpcl │ ├── const_int.mpcl │ ├── const_mod.mpcl │ ├── const_slice_arr_0.mpcl │ ├── const_slice_arr_1.mpcl │ ├── copy_make.mpcl │ ├── copy_ptr.mpcl │ ├── copy_slice_eq.mpcl │ ├── copy_slice_gt.mpcl │ ├── copy_slice_lt.mpcl │ ├── divi.mpcl │ ├── divu.mpcl │ ├── for.mpcl │ ├── len_array.mpcl │ ├── len_array_sum.mpcl │ ├── len_string.mpcl │ ├── len_string_sum.mpcl │ ├── lshift0.mpcl │ ├── lshift1.mpcl │ ├── lshift64.mpcl │ ├── make.mpcl │ ├── modi.mpcl │ ├── modu.mpcl │ ├── mult.mpcl │ ├── named_return.mpcl │ ├── named_return2.mpcl │ ├── named_return3.mpcl │ ├── pkg.mpcl │ ├── ptr.mpcl │ ├── ptr_array.mpcl │ ├── ptr_array_get.mpcl │ ├── ptr_arrays.mpcl │ ├── ptr_scopes.mpcl │ ├── ptr_struct_field.mpcl │ ├── rshift0.mpcl │ ├── rshift1.mpcl │ ├── rshift64.mpcl │ ├── sub.mpcl │ ├── test_ge.mpcl │ ├── test_gt.mpcl │ ├── test_le.mpcl │ ├── test_lt.mpcl │ ├── var.mpcl │ ├── var2.mpcl │ ├── var3.mpcl │ ├── var4.mpcl │ └── zerolabel.mpcl └── math │ └── bits │ ├── rotate_left_16.mpcl │ └── rotate_left_32.mpcl ├── testsuite_test.go └── types ├── parse.go ├── parse_test.go ├── types.go └── types_test.go /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | name: "CodeQL" 7 | 8 | on: 9 | push: 10 | branches: [master] 11 | pull_request: 12 | # The branches below must be a subset of the branches above 13 | branches: [master] 14 | schedule: 15 | - cron: '0 4 * * 3' 16 | 17 | jobs: 18 | analyze: 19 | name: Analyze 20 | runs-on: ubuntu-latest 21 | 22 | strategy: 23 | fail-fast: false 24 | matrix: 25 | # Override automatic language detection by changing the below list 26 | # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] 27 | language: ['go'] 28 | # Learn more... 29 | # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection 30 | 31 | steps: 32 | - name: Checkout repository 33 | uses: actions/checkout@v2 34 | with: 35 | # We must fetch at least the immediate parents so that if this is 36 | # a pull request then we can checkout the head. 37 | fetch-depth: 2 38 | 39 | # If this run was triggered by a pull request event, then checkout 40 | # the head of the pull request instead of the merge commit. 41 | - run: git checkout HEAD^2 42 | if: ${{ github.event_name == 'pull_request' }} 43 | 44 | # Initializes the CodeQL tools for scanning. 45 | - name: Initialize CodeQL 46 | uses: github/codeql-action/init@v1 47 | with: 48 | languages: ${{ matrix.language }} 49 | # If you wish to specify custom queries, you can do so here or in a config file. 50 | # By default, queries listed here will override any specified in a config file. 51 | # Prefix the list here with "+" to use these queries and those in the config file. 52 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 53 | 54 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 55 | # If this step fails, then you should remove it and run the build manually (see below) 56 | - name: Autobuild 57 | uses: github/codeql-action/autobuild@v1 58 | 59 | # ℹ️ Command-line programs to run using the OS shell. 60 | # 📚 https://git.io/JvXDl 61 | 62 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 63 | # and modify them (or add more) to build your code if your project 64 | # uses a compiled language 65 | 66 | #- run: | 67 | # make bootstrap 68 | # make release 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v1 72 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go 2 | on: [push] 3 | jobs: 4 | 5 | build: 6 | name: Build 7 | runs-on: ${{ matrix.os }} 8 | strategy: 9 | matrix: 10 | os: [ubuntu-latest, macos-latest] 11 | steps: 12 | 13 | - name: Set up Go 1.21 14 | uses: actions/setup-go@v1 15 | with: 16 | go-version: 1.21 17 | id: go 18 | 19 | - name: Check out code into the Go module directory 20 | uses: actions/checkout@v2 21 | 22 | - name: Get dependencies 23 | run: | 24 | go get -v -t -d ./... 25 | 26 | - name: Build 27 | run: go build -v ./... 28 | 29 | - name: Test 30 | run: go test ./... 31 | 32 | - name: Vet 33 | run: go vet ./... 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | .DS_Store 15 | ,* 16 | *~ 17 | *.dot 18 | *.pdf 19 | *.ssa 20 | *.prof 21 | *.bristol 22 | apps/circuit/circuit 23 | apps/garbled/garbled 24 | apps/iotest/iotest 25 | apps/iter/iter 26 | apps/mpcldoc/mpcldoc 27 | apps/objdump/objdump 28 | apps/ot/ot 29 | docs/ref/ecdh/ecdh 30 | docs/ref/ed25519/ed25519 31 | docs/ref/sha1/sha1 32 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | 2 | BLOG := ../blog 3 | OUTPUT = ,apidoc 4 | 5 | all: 6 | @echo "Targets: apidoc public" 7 | 8 | apidoc: 9 | $(BLOG)/blog -site -lib $(BLOG) -draft -t templates/mpcl -o $(OUTPUT) docs/apidoc/ 10 | ./apps/mpcldoc/mpcldoc -dir $(OUTPUT) pkg apps/garbled/examples 11 | 12 | public: 13 | make apidoc OUTPUT=$(HOME)/work/www/mpcl 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Markku Rossi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /apps/.gitignore: -------------------------------------------------------------------------------- 1 | *.circ 2 | -------------------------------------------------------------------------------- /apps/circuit/GNUmakefile: -------------------------------------------------------------------------------- 1 | CIRCUITS=$(wildcard *.circ) 2 | PDFS=$(patsubst %.circ,%.pdf,$(CIRCUITS)) 3 | PNGS=$(patsubst %.circ,%.png,$(CIRCUITS)) 4 | 5 | all: $(PDFS) $(PNGS) 6 | 7 | clean: 8 | $(RM) $(PDFS) $(PNGS) 9 | 10 | %.pdf: %.eps 11 | ps2pdf -sPAPERSIZE=a4 $< 12 | 13 | %.eps: %.dot 14 | dot -Tps -Gorientation=land -Gsize="11,7.5" $< > $@ 15 | 16 | %.png: %.dot 17 | dot -Tpng $< > $@ 18 | 19 | %.dot: %.circ 20 | ./circuit $< > $@ 21 | -------------------------------------------------------------------------------- /apps/circuit/and.circ: -------------------------------------------------------------------------------- 1 | 1 3 2 | 2 1 1 3 | 1 1 4 | 5 | 2 1 0 1 2 AND 6 | -------------------------------------------------------------------------------- /apps/circuit/main.go: -------------------------------------------------------------------------------- 1 | // 2 | // main.go 3 | // 4 | // Copyright (c) 2019-2022 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package main 10 | 11 | import ( 12 | "flag" 13 | "fmt" 14 | "log" 15 | 16 | "github.com/markkurossi/mpc/circuit" 17 | ) 18 | 19 | func main() { 20 | flag.Parse() 21 | 22 | for _, file := range flag.Args() { 23 | c, err := circuit.Parse(file) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | fmt.Printf("digraph circuit\n{\n") 29 | fmt.Printf(" overlap=scale;\n") 30 | fmt.Printf(" node\t[fontname=\"Helvetica\"];\n") 31 | fmt.Printf(" {\n node [shape=plaintext];\n") 32 | for w := 0; w < c.NumWires; w++ { 33 | fmt.Printf(" w%d\t[label=\"%d\"];\n", w, w) 34 | } 35 | fmt.Printf(" }\n") 36 | 37 | fmt.Printf(" {\n node [shape=box];\n") 38 | for idx, gate := range c.Gates { 39 | fmt.Printf(" g%d\t[label=\"%s\"];\n", idx, gate.Op) 40 | } 41 | fmt.Printf(" }\n") 42 | 43 | if true { 44 | fmt.Printf(" { rank=same") 45 | for w := 0; w < c.Inputs.Size(); w++ { 46 | fmt.Printf("; w%d", w) 47 | } 48 | fmt.Printf(";}\n") 49 | 50 | fmt.Printf(" { rank=same") 51 | for w := 0; w < c.Outputs.Size(); w++ { 52 | fmt.Printf("; w%d", c.NumWires-w-1) 53 | } 54 | fmt.Printf(";}\n") 55 | } 56 | 57 | for idx, gate := range c.Gates { 58 | for _, i := range gate.Inputs() { 59 | fmt.Printf(" w%d -> g%d;\n", i, idx) 60 | } 61 | fmt.Printf(" g%d -> w%d;\n", idx, gate.Output) 62 | } 63 | fmt.Printf("}\n") 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /apps/circuit/not.circ: -------------------------------------------------------------------------------- 1 | 1 2 2 | 2 1 0 3 | 1 1 4 | 5 | 1 1 0 1 INV 6 | -------------------------------------------------------------------------------- /apps/garbled/GNUmakefile: -------------------------------------------------------------------------------- 1 | 2 | programs := $(wildcard examples/*.mpcl) 3 | circs := $(patsubst %.mpcl,%.circ,$(programs)) 4 | mpclcs := $(patsubst %.mpcl,%.mpclc,$(programs)) 5 | bristols := $(patsubst %.mpcl,%.bristol,$(programs)) 6 | ssas := $(patsubst %.mpcl,%.ssa,$(programs)) 7 | dots := $(patsubst %,%.dot,$(circs) $(mpclcs) $(bristols) $(ssas)) 8 | svgs := $(patsubst %.dot,%.svg,$(dots)) 9 | 10 | generated_dots := $(wildcard examples/*.dot) 11 | generated_svgs := $(patsubst %.dot,%.svg,$(generated_dots)) 12 | 13 | all: $(generated_svgs) 14 | 15 | %.svg: %.dot 16 | dot -Tsvg $+ > $@ 17 | 18 | clean: 19 | @rm -f $(circs) $(mpclcs) $(bristols) $(ssas) $(dots) $(svgs) 20 | -------------------------------------------------------------------------------- /apps/garbled/bmr.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "log" 12 | 13 | "github.com/markkurossi/mpc" 14 | "github.com/markkurossi/mpc/circuit" 15 | "github.com/markkurossi/mpc/compiler/utils" 16 | "github.com/markkurossi/mpc/p2p" 17 | ) 18 | 19 | func bmrMode(file string, params *utils.Params, player int) error { 20 | fmt.Printf("semi-honest secure BMR protocol\n") 21 | fmt.Printf("player: %d\n", player) 22 | 23 | circ, err := loadCircuit(file, params, nil) 24 | if err != nil { 25 | return err 26 | } 27 | 28 | if player >= len(circ.Inputs) { 29 | return fmt.Errorf("invalid party number %d for %d-party computation", 30 | player, len(circ.Inputs)) 31 | } 32 | 33 | input, err := circ.Inputs[player].Parse(inputFlag) 34 | if err != nil { 35 | return err 36 | } 37 | 38 | for idx, arg := range circ.Inputs { 39 | if idx == player { 40 | fmt.Printf(" + In%d: %s\n", idx, arg) 41 | } else { 42 | fmt.Printf(" - In%d: %s\n", idx, arg) 43 | } 44 | } 45 | 46 | fmt.Printf(" - Out: %s\n", circ.Outputs) 47 | fmt.Printf(" - In: %s\n", inputFlag) 48 | 49 | // Create network. 50 | addr := makeAddr(player) 51 | nw, err := p2p.NewNetwork(addr, player) 52 | if err != nil { 53 | return err 54 | } 55 | defer nw.Close() 56 | 57 | numPlayers := len(circ.Inputs) 58 | 59 | for i := 0; i < numPlayers; i++ { 60 | if i == player { 61 | continue 62 | } 63 | err := nw.AddPeer(makeAddr(i), i) 64 | if err != nil { 65 | return err 66 | } 67 | } 68 | 69 | log.Printf("Network created\n") 70 | 71 | result, err := circuit.Player(nw, circ, input, verbose) 72 | if err != nil { 73 | return err 74 | } 75 | 76 | mpc.PrintResults(result, circ.Outputs) 77 | return nil 78 | } 79 | 80 | func makeAddr(player int) string { 81 | return fmt.Sprintf("127.0.0.1:%d", 8080+player) 82 | } 83 | -------------------------------------------------------------------------------- /apps/garbled/default.pgo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markkurossi/mpc/8a4e258ee2b98a9e96e8f18193a6813dc3281d36/apps/garbled/default.pgo -------------------------------------------------------------------------------- /apps/garbled/examples/.gitignore: -------------------------------------------------------------------------------- 1 | *.svg 2 | *.mpclc 3 | -------------------------------------------------------------------------------- /apps/garbled/examples/3party.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | // Sample 3-party circuit where each party provides their input bit 4 | // and the result is bitwise AND of the inputs. 5 | package main 6 | 7 | func main(a, b, e uint1) uint { 8 | return a & b & e 9 | } 10 | -------------------------------------------------------------------------------- /apps/garbled/examples/add.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | // Example of how to add two uint256 values. You can use any data type for the input as long as it is divisible by 8, 5 | // such as uint512. 6 | // 7 | // To run the Evaluator: 8 | // 9 | // ./garbled -e -i 0xb33d6a91b4ca8ac31c639c6742cba5a74c661a63311548af191c298a945d4891 examples/add.mpcl 10 | // 11 | // To run the Garbler: 12 | // 13 | // ./garbled -i 0x5bf6db5927d799cf225f165e9508238edc5a1200fcad08c6411648733eb3100f examples/add.mpcl 14 | // 15 | // The expected result should be: 16 | // 6877051328478326342308659403308568813546041258230645271156059935820089415840 (0x0f3445eadca224923ec2b2c5d7d3c93628c02c642dc251755a3271fdd31058a0) 17 | 18 | package main 19 | 20 | import ( 21 | "math" 22 | ) 23 | 24 | func main(a, b uint256) uint { 25 | return a + b 26 | } 27 | -------------------------------------------------------------------------------- /apps/garbled/examples/aesblock.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/aes" 7 | ) 8 | 9 | func main(key, data [16]byte) []byte { 10 | return aes.EncryptBlock(key, data) 11 | } 12 | -------------------------------------------------------------------------------- /apps/garbled/examples/aesblock2.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/aes" 7 | ) 8 | 9 | func main(key, data [16]byte) []byte { 10 | return aes.Block128(key, data) 11 | } 12 | -------------------------------------------------------------------------------- /apps/garbled/examples/aescbc.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/cipher/cbc" 7 | ) 8 | 9 | // Case #1: Encrypting 16 bytes (1 block) using AES-CBC with 128-bit key 10 | // 11 | // Key : 0x06a9214036b8a15b512e03d534120006 12 | // IV : 0x3dafba429d9eb430b422da802c9fac41 13 | // Plaintext : "Single block msg" 14 | // Ciphertext: 0xe353779c1079aeb82708942dbe77181a 15 | // 16 | // Case #2: Encrypting 32 bytes (2 blocks) using AES-CBC with 128-bit key 17 | // 18 | // Key : 0xc286696d887c9aa0611bbb3e2025a45a 19 | // IV : 0x562e17996d093d28ddb3ba695a2e6f58 20 | // Plaintext : 0x000102030405060708090a0b0c0d0e0f 21 | // 101112131415161718191a1b1c1d1e1f 22 | // Ciphertext: 0xd296cd94c2cccf8a3a863028b5e1dc0a 23 | // 24 | // 7586602d253cfff91b8266bea6d61ab1 25 | func main(g, e [16]byte) []byte { 26 | key := []byte{ 27 | 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, 28 | 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06, 29 | } 30 | iv := []byte{ 31 | 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, 32 | 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41, 33 | } 34 | plain := []byte("Single block msg") 35 | 36 | key2 := []byte{ 37 | 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0, 38 | 0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a, 39 | } 40 | iv2 := []byte{ 41 | 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28, 42 | 0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58, 43 | } 44 | plain2 := []byte{ 45 | 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 46 | 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 47 | 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 48 | 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 49 | } 50 | 51 | //return cbc.EncryptAES128(key, iv, plain) 52 | //return cbc.EncryptAES128(key2, iv2, plain2) 53 | return cbc.EncryptAES128(g, iv, e) 54 | } 55 | -------------------------------------------------------------------------------- /apps/garbled/examples/aesexpand.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/aes" 7 | ) 8 | 9 | func main(key, data [16]byte) []uint { 10 | return aes.ExpandEncryptionKey(key) 11 | } 12 | -------------------------------------------------------------------------------- /apps/garbled/examples/aesgcm.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "bytes" 7 | "crypto/cipher/gcm" 8 | ) 9 | 10 | func main(a, b byte) (string, int) { 11 | key := []byte{ 12 | 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, 13 | 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06, 14 | } 15 | nonce := []byte{ 16 | 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, 17 | 0xb4, 0x22, 0xda, 0x80, 18 | } 19 | plain := []byte("Single block msgSingle block msg") 20 | additional := []byte("additional data to be authenticated") 21 | 22 | c := gcm.EncryptAES128(key, nonce, plain, additional) 23 | p, ok := gcm.DecryptAES128(key, nonce, c, additional) 24 | if !ok { 25 | return "Open failed", 0 26 | } 27 | return string(p), bytes.Compare(plain, p) 28 | } 29 | -------------------------------------------------------------------------------- /apps/garbled/examples/and.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | func main(a, b uint1) uint1 { 6 | return a & b 7 | } 8 | -------------------------------------------------------------------------------- /apps/garbled/examples/credit.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | type Size = uint32 6 | 7 | type Applicant struct { 8 | male bool 9 | age Size 10 | income Size 11 | } 12 | 13 | type Bank struct { 14 | maxAge Size 15 | femaleIncome Size 16 | maleIncome Size 17 | } 18 | 19 | func main(applicant Applicant, bank Bank) bool { 20 | // Bank sets the maximum age limit. 21 | if applicant.age > bank.maxAge { 22 | return false 23 | } 24 | if applicant.male { 25 | // Credit criteria for males. 26 | return applicant.age >= 21 && applicant.income >= bank.maleIncome 27 | } else { 28 | // Credit criteria for females. 29 | return applicant.age >= 18 && applicant.income >= bank.femaleIncome 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /apps/garbled/examples/div.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | package main 5 | 6 | import ( 7 | "math" 8 | ) 9 | 10 | func main(a, b uint64) uint { 11 | return a / b 12 | 13 | //return int64(math.DivUint64(uint64(a), uint64(b))) 14 | } 15 | -------------------------------------------------------------------------------- /apps/garbled/examples/ecdh/keygen.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/curve25519" 7 | ) 8 | 9 | func main(g, e [32]byte) ([]byte, []byte, []byte) { 10 | // var s [32]byte 11 | // curve25519.ScalarMult(&s, &g, &e) 12 | // return g, e, s 13 | 14 | var privateKey [32]byte 15 | for i := 0; i < len(privateKey); i++ { 16 | //privateKey[i] = g[i] ^ e[i] 17 | privateKey[i] = (i % 8) + 1 18 | } 19 | 20 | var publicKey [32]byte 21 | curve25519.ScalarBaseMult(&publicKey, &privateKey) 22 | 23 | var privateKey2 [32]byte 24 | for i := 0; i < len(privateKey2); i++ { 25 | //privateKey[i] = g[i] ^ e[i] 26 | privateKey2[i] = (i % 16) + 1 27 | } 28 | 29 | var publicKey2 [32]byte 30 | curve25519.ScalarBaseMult(&publicKey2, &privateKey2) 31 | 32 | var secret [32]byte 33 | curve25519.ScalarMult(&secret, &privateKey, &publicKey2) 34 | 35 | return publicKey, publicKey2, secret 36 | } 37 | -------------------------------------------------------------------------------- /apps/garbled/examples/ed25519/sign.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | // This example implements Ed25519 signature computation. The Ed25519 4 | // keypair is: 5 | // 6 | // pub : 8ae64963506002e267a59665e9a2e6f9348cc159be53747894478e182ece9fcb 7 | // 8 | // priv : 2f8d55706c0cb226d75aafe2f4c4648e5cd32bd51fbc9764d54908c67812aee2 9 | // 8ae64963506002e267a59665e9a2e6f9348cc159be53747894478e182ece9fcb 10 | // 11 | // The Garbler and Evaluator share the private key as two random 12 | // shares. The private key is contructed during the signature 13 | // computation by XOR:ing the random shares together: 14 | // 15 | // privG: a66e6bb15b6ad6b19bacf163573d0179de7f62bafcd6aba521d525a0d7b79f7e 16 | // 153801bc47f6d566a274e370f615f140f20202ab80ec88fdd611b726b8526726 17 | // 18 | // 19 | // privE: 89e33ec1376664974cf65e81a3f965f782ac496fe36a3cc1f49c2d66afa5319c 20 | // 9fde48df1796d784c5d175151fb717b9c68ec3f23ebffc854256393e969cf8ed 21 | // 22 | // priv = privG ^ privE 23 | // 24 | // Run the Evaluator with one input: the Evaluator's private key share: 25 | // 26 | // ./garbled -e -v -stream -i 0x89e33ec1376664974cf65e81a3f965f782ac496fe36a3cc1f49c2d66afa5319c9fde48df1796d784c5d175151fb717b9c68ec3f23ebffc854256393e969cf8ed 27 | // 28 | // The Garbler takes two inputs: the message to sign, and the 29 | // Garbler's private key share: 30 | // 31 | // ./garbled -stream -v -i 0x4d61726b6b7520526f737369203c6d747240696b692e66693e2068747470733a2f2f7777772e6d61726b6b75726f7373692e636f6d2f,0xa66e6bb15b6ad6b19bacf163573d0179de7f62bafcd6aba521d525a0d7b79f7e153801bc47f6d566a274e370f615f140f20202ab80ec88fdd611b726b8526726 examples/ed25519/sign.mpcl 32 | // 33 | // The result signature is: 34 | // 35 | // Result[0]: f43c1cf1755345852211942af0838414334eec9cbf36e26a9f9e8d4bb720deb145ffbeec82249c875116757441206bcdc56b501e750f1f590917d772dfee980f 36 | package main 37 | 38 | import ( 39 | "crypto/ed25519" 40 | ) 41 | 42 | type Garbler struct { 43 | msg [64]byte 44 | privShare [64]byte 45 | } 46 | 47 | func main(g Garbler, privShare [64]byte) []byte { 48 | var priv [64]byte 49 | 50 | for i := 0; i < len(priv); i++ { 51 | priv[i] = g.privShare[i] ^ privShare[i] 52 | } 53 | 54 | return ed25519.Sign(priv, g.msg) 55 | } 56 | -------------------------------------------------------------------------------- /apps/garbled/examples/encrypt.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | // Example how to encrypt fixed sized data with AES-128-GCM. 4 | // 5 | // Run the Evaluator with two inputs: evaluator's key and nonce shares: 6 | // 7 | // ./garbled -e -i 0x8cd98b88adab08d6d60fe57c8b8a33f3,0xfd5e0f8f155e7102aa526ad0 examples/encrypt.mpcl 8 | // 9 | // The Garbler takes three arguments: the message to encrypt, and its 10 | // key and nonce shares: 11 | // 12 | // ./garbled -i 0x48656c6c6f2c20776f726c6421,0xed800b17b0c9d2334b249332155ddef5,0xa300751458c775a08762c2cd examples/encrypt.mpcl 13 | package main 14 | 15 | import ( 16 | "crypto/cipher/gcm" 17 | ) 18 | 19 | type Garbler struct { 20 | msg [64]byte 21 | keyShare [16]byte 22 | nonceShare [12]byte 23 | } 24 | 25 | type Evaluator struct { 26 | keyShare [16]byte 27 | nonceShare [12]byte 28 | } 29 | 30 | func main(g Garbler, e Evaluator) []byte { 31 | var key [16]byte 32 | 33 | for i := 0; i < len(key); i++ { 34 | key[i] = g.keyShare[i] ^ e.keyShare[i] 35 | } 36 | 37 | var nonce [12]byte 38 | 39 | for i := 0; i < len(nonce); i++ { 40 | nonce[i] = g.nonceShare[i] ^ e.nonceShare[i] 41 | } 42 | 43 | return gcm.EncryptAES128(key, nonce, g.msg, []byte("unused")) 44 | } 45 | -------------------------------------------------------------------------------- /apps/garbled/examples/hamming.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "encoding/binary" 7 | ) 8 | 9 | func main(a, b uint1024) uint { 10 | return binary.HammingDistance(a, b) 11 | } 12 | -------------------------------------------------------------------------------- /apps/garbled/examples/hmac-sha256.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | // This example computes HMAC-SHA256 where the HMAC key is shared as 4 | // two random shares between garbler and evaluator. The garbler's key 5 | // share is: 6 | // 7 | // keyG: 4de216d2fdc9301e5b9c78486f7109a05670d200d9e2f275ec0aad08ec42af47 8 | // fcb59bf460d50b01333a748f3a9efb13e08036d49a26c21ba2e33a5f8a2cf0e7 9 | // 10 | // The evaluator's key share is: 11 | // 12 | // keyE: f87a00ef89c2396de32f6ac0748f6fa1b641013d46f74ce25cc625904215a675 13 | // 01c0c7196a2602f6516527958a82271847933c35d170d98bfdb04d2ddf3bb197 14 | // 15 | // The final HMAC key is keyG ^ keyE: 16 | // 17 | // key : b598163d740b0973b8b312881bfe6601e031d33d9f15be97b0cc8898ae570932 18 | // fd755ced0af309f7625f531ab01cdc0ba7130ae14b561b905f53777255174170 19 | // 20 | // The example uses 32-byte messages (Garbler.msg) so with the message: 21 | // 22 | // msg : Hello, world!................... 23 | // hex : 48656c6c6f2c20776f726c64212e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e 24 | // 25 | // We expect to get the following HMAC-SHA256 output: 26 | // 27 | // sum : 60d27dbd14f1e351f20069171fead00ef557d17ac9a41d02baa488ca4b90171a 28 | // 29 | // Now we can run the MPC computation as follows. First, run the 30 | // evaluator with one input: the evaluator's key share: 31 | // 32 | // ./garbled -e -v -i 0xf87a00ef89c2396de32f6ac0748f6fa1b641013d46f74ce25cc625904215a67501c0c7196a2602f6516527958a82271847933c35d170d98bfdb04d2ddf3bb197 examples/hmac-sha256.mpcl 33 | // 34 | // The garbler takes two inputs: the message and the garbler's key 35 | // share: 36 | // 37 | // ./garbled -v -i 0x48656c6c6f2c20776f726c64212e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e,0x4de216d2fdc9301e5b9c78486f7109a05670d200d9e2f275ec0aad08ec42af47fcb59bf460d50b01333a748f3a9efb13e08036d49a26c21ba2e33a5f8a2cf0e7 examples/hmac-sha256.mpcl 38 | // 39 | // The MCP computation providers the expected HMAC result: 40 | // 41 | // Result[0]: 60d27dbd14f1e351f20069171fead00ef557d17ac9a41d02baa488ca4b90171a 42 | package main 43 | 44 | import ( 45 | "crypto/hmac" 46 | ) 47 | 48 | type Garbler struct { 49 | msg []byte 50 | keyShare [64]byte 51 | } 52 | 53 | func main(g Garbler, eKeyShare [64]byte) []byte { 54 | var key [64]byte 55 | 56 | for i := 0; i < len(key); i++ { 57 | key[i] = g.keyShare[i] ^ eKeyShare[i] 58 | } 59 | 60 | return hmac.SumSHA256(g.msg, key) 61 | } 62 | -------------------------------------------------------------------------------- /apps/garbled/examples/hmac-sha512.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | // This example computes HMAC-SHA256 where the HMAC key is shared as 4 | // two random shares between garbler and evaluator. The garbler's key 5 | // share is: 6 | // 7 | // keyG: 4de216d2fdc9301e5b9c78486f7109a05670d200d9e2f275ec0aad08ec42af47 8 | // fcb59bf460d50b01333a748f3a9efb13e08036d49a26c21ba2e33a5f8a2cf0e7 9 | // 10 | // The evaluator's key share is: 11 | // 12 | // keyE: f87a00ef89c2396de32f6ac0748f6fa1b641013d46f74ce25cc625904215a675 13 | // 01c0c7196a2602f6516527958a82271847933c35d170d98bfdb04d2ddf3bb197 14 | // 15 | // The final HMAC key is keyG ^ keyE: 16 | // 17 | // key : b598163d740b0973b8b312881bfe6601e031d33d9f15be97b0cc8898ae570932 18 | // fd755ced0af309f7625f531ab01cdc0ba7130ae14b561b905f53777255174170 19 | // 20 | // The example uses 32-byte messages (Garbler.msg) so with the message: 21 | // 22 | // msg : Hello, world!................... 23 | // hex : 48656c6c6f2c20776f726c64212e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e 24 | // 25 | // We expect to get the following HMAC-SHA256 output: 26 | // 27 | // sum : 89c648c4d7b4220f6767706dec64f69bbdb2725d062a09a447b9cf32af8636ee8853f92ca59c0e81712b72c79f3503f7f2131d20c7a5dfae87b79f839cecf2c4 28 | // 29 | // Now we can run the MPC computation as follows. First, run the 30 | // evaluator with one input: the evaluator's key share: 31 | // 32 | // ./garbled -e -v -i 0xf87a00ef89c2396de32f6ac0748f6fa1b641013d46f74ce25cc625904215a67501c0c7196a2602f6516527958a82271847933c35d170d98bfdb04d2ddf3bb197 examples/hmac-sha512.mpcl 33 | // 34 | // The garbler takes two inputs: the message and the garbler's key 35 | // share: 36 | // 37 | // ./garbled -v -i 0x48656c6c6f2c20776f726c64212e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e2e,0x4de216d2fdc9301e5b9c78486f7109a05670d200d9e2f275ec0aad08ec42af47fcb59bf460d50b01333a748f3a9efb13e08036d49a26c21ba2e33a5f8a2cf0e7 examples/hmac-sha512.mpcl 38 | // 39 | // The MCP computation providers the expected HMAC result: 40 | // 41 | // Result[0]: 89c648c4d7b4220f6767706dec64f69bbdb2725d062a09a447b9cf32af8636ee8853f92ca59c0e81712b72c79f3503f7f2131d20c7a5dfae87b79f839cecf2c4 42 | 43 | package main 44 | 45 | import ( 46 | "crypto/hmac" 47 | ) 48 | 49 | type Garbler struct { 50 | msg []byte 51 | keyShare [64]byte 52 | } 53 | 54 | func main(g Garbler, eKeyShare [64]byte) []byte { 55 | var key [64]byte 56 | 57 | for i := 0; i < len(key); i++ { 58 | key[i] = g.keyShare[i] ^ eKeyShare[i] 59 | } 60 | 61 | return hmac.SumSHA512(g.msg, key) 62 | } 63 | -------------------------------------------------------------------------------- /apps/garbled/examples/millionaire.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | // Yao's Millionaires' problem with int64 values. 5 | package main 6 | 7 | func main(a, b int64) bool { 8 | if a > b { 9 | return true 10 | } else { 11 | return false 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/garbled/examples/montgomery.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | // RSA encryption with Montgomery modular multiplication. 5 | // 6 | // ./garbled -e -v -i 0x321af130 examples/montgomery.mpcl 7 | // ./garbled -v -i 0x6d7472,9,0xd60b2b09,0x10001 examples/montgomery.mpcl 8 | package main 9 | 10 | import ( 11 | "math" 12 | ) 13 | 14 | type Size = uint64 15 | 16 | type Garbler struct { 17 | msg Size 18 | privShare Size 19 | pubN Size 20 | pubE Size 21 | } 22 | 23 | func main(g Garbler, privShare Size) (uint, uint) { 24 | 25 | priv := g.privShare + privShare 26 | 27 | cipher := Encrypt(g.msg, g.pubE, g.pubN) 28 | plain := Decrypt(cipher, priv, g.pubN) 29 | 30 | return cipher, plain 31 | } 32 | 33 | func Encrypt(msg, e, n uint) uint { 34 | return math.ExpMontgomery(msg, e, n) 35 | } 36 | 37 | func Decrypt(cipher, d, n uint) uint { 38 | return math.ExpMontgomery(cipher, d, n) 39 | } 40 | -------------------------------------------------------------------------------- /apps/garbled/examples/mult.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | package main 5 | 6 | func main(a, b uint64) uint { 7 | return a * b 8 | } 9 | -------------------------------------------------------------------------------- /apps/garbled/examples/mult1024.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | package main 5 | 6 | func main(a, b int1024) int1024 { 7 | return a * b 8 | } 9 | -------------------------------------------------------------------------------- /apps/garbled/examples/rps.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Rock, Paper, Scissors 4 | 5 | package main 6 | 7 | func main(g, e int8) string { 8 | if g == e { 9 | return "-" 10 | } 11 | if (g+1)%3 == e { 12 | return "evaluator" 13 | } 14 | return "garbler" 15 | } 16 | -------------------------------------------------------------------------------- /apps/garbled/examples/rsa.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | // 32-bit RSA encryption and decryption. 5 | // 6 | // The key parameters are: 7 | // 8 | // d: 0x321af139 9 | // n: 0xd60b2b09 10 | // e: 0x10001 11 | // 12 | // private: d, n 13 | // public: e, n 14 | // 15 | // msg: 0x6d7472 16 | // cipher: 0x61f9ef88 17 | // 18 | // Run garbler and evaluator as follows: 19 | // 20 | // ./garbled -e -v -i 9 examples/rsa.mpcl 21 | // ./garbled -v -i 0x6d7472,0x321af130,0xd60b2b09,0x10001 examples/rsa.mpcl 22 | package main 23 | 24 | import ( 25 | "crypto/rsa" 26 | ) 27 | 28 | type Size = uint32 29 | 30 | type Garbler struct { 31 | msg Size 32 | privShare Size 33 | pubN Size 34 | pubE Size 35 | } 36 | 37 | func main(g Garbler, privShare Size) (uint, uint) { 38 | 39 | priv := g.privShare + privShare 40 | 41 | cipher := rsa.Encrypt(g.msg, g.pubE, g.pubN) 42 | plain := rsa.Decrypt(cipher, priv, g.pubN) 43 | 44 | return cipher, plain 45 | } 46 | -------------------------------------------------------------------------------- /apps/garbled/examples/rsasign.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | // RSA signature with Size bits. 5 | // 6 | // The key parameters are: 7 | // 8 | // d: 0x321af139 9 | // n: 0xd60b2b09 10 | // e: 0x10001 11 | // 12 | // private: d, n 13 | // public: e, n 14 | // 15 | // msg: 0x6d7472 16 | // signature: 0x55a83b79 17 | // 18 | // Run garbler and evaluator as follows: 19 | // 20 | // ./garbled -e -v -i 9 examples/rsasign.mpcl 21 | // ./garbled -v -i 0x6d7472,0x321af130,0xd60b2b09,0x10001 examples/rsasign.mpcl 22 | package main 23 | 24 | import ( 25 | "crypto/rsa" 26 | ) 27 | 28 | type Size = uint512 29 | 30 | type Garbler struct { 31 | msg Size 32 | privShare Size 33 | pubN Size 34 | pubE Size 35 | } 36 | 37 | func main(g Garbler, privShare Size) uint { 38 | 39 | priv := g.privShare + privShare 40 | 41 | return rsa.Decrypt(g.msg, priv, g.pubN) 42 | } 43 | -------------------------------------------------------------------------------- /apps/garbled/examples/sort.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "sort" 7 | ) 8 | 9 | var input = []int9{ 10 | 136, 142, 146, 165, 183, 189, 220, 223, 232, 235, 67, 73, 77, 88, 11 | 91, 93, 95, 97, 98, 132, 5, 6, 7, 10, 14, 18, 18, 37, 50, 64, 245, 12 | 249, 252, 136, 142, 146, 165, 183, 189, 220, 223, 232, 235, 67, 13 | 73, 77, 88, 91, 93, 95, 97, 98, 132, 136, 142, 146, 165, 183, 189, 14 | 220, 223, 232, 235, 67, 73, 77, 88, 91, 93, 95, 97, 98, 132, 5, 6, 15 | 7, 10, 14, 18, 18, 37, 50, 64, 245, 249, 252, 136, 142, 146, 165, 16 | 183, 189, 220, 223, 232, 235, 67, 73, 77, 88, 91, 93, 95, 97, 98, 17 | 132, 136, 142, 146, 165, 183, 189, 220, 223, 232, 235, 67, 73, 77, 18 | 88, 91, 93, 95, 97, 98, 132, 5, 6, 7, 10, 14, 18, 18, 37, 50, 64, 19 | 245, 249, 252, 136, 142, 146, 165, 183, 189, 220, 223, 232, 235, 20 | 67, 73, 77, 88, 91, 93, 95, 97, 98, 132, 5, 6, 7, 10, 14, 18, 18, 21 | 37, 50, 64, 245, 249, 252, 136, 142, 146, 165, 183, 189, 220, 223, 22 | 232, 235, 67, 73, 77, 88, 91, 93, 95, 97, 98, 132, 136, 142, 146, 23 | 165, 183, 189, 220, 223, 232, 235, 67, 73, 77, 88, 91, 93, 95, 97, 24 | 98, 132, 5, 6, 7, 10, 14, 18, 18, 37, 50, 64, 245, 249, 252, 136, 25 | 142, 146, 165, 183, 189, 220, 223, 232, 235, 67, 73, 77, 88, 91, 26 | 93, 95, 97, 98, 132, 136, 142, 146, 165, 183, 189, 220, 223, 232, 27 | 235, 67, 73, 77, 88, 91, 93, 95, 97, 98, 132, 5, 6, 7, 10, 14, 18, 28 | 18, 37, 50, 64, 245, 249, 252, 29 | } 30 | 31 | func main(g, e byte) []int { 32 | return sort.Reverse(sort.Slice(input)) 33 | } 34 | -------------------------------------------------------------------------------- /apps/garbled/streaming.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package main 8 | 9 | import ( 10 | "fmt" 11 | "io" 12 | "net" 13 | "strings" 14 | 15 | "github.com/markkurossi/mpc" 16 | "github.com/markkurossi/mpc/circuit" 17 | "github.com/markkurossi/mpc/compiler" 18 | "github.com/markkurossi/mpc/compiler/utils" 19 | "github.com/markkurossi/mpc/ot" 20 | "github.com/markkurossi/mpc/p2p" 21 | ) 22 | 23 | func streamEvaluatorMode(oti ot.OT, input input, once bool) error { 24 | inputSizes, err := circuit.InputSizes(input) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | ln, err := net.Listen("tcp", port) 30 | if err != nil { 31 | return err 32 | } 33 | fmt.Printf("Listening for connections at %s\n", port) 34 | 35 | for { 36 | nc, err := ln.Accept() 37 | if err != nil { 38 | return err 39 | } 40 | fmt.Printf("New connection from %s\n", nc.RemoteAddr()) 41 | 42 | conn := p2p.NewConn(nc) 43 | 44 | err = conn.SendInputSizes(inputSizes) 45 | if err != nil { 46 | conn.Close() 47 | return err 48 | } 49 | err = conn.Flush() 50 | if err != nil { 51 | conn.Close() 52 | return err 53 | } 54 | 55 | outputs, result, err := circuit.StreamEvaluator(conn, oti, input, 56 | verbose) 57 | conn.Close() 58 | 59 | if err != nil && err != io.EOF { 60 | return fmt.Errorf("%s: %v", nc.RemoteAddr(), err) 61 | } 62 | 63 | mpc.PrintResults(result, outputs) 64 | if once { 65 | return nil 66 | } 67 | } 68 | } 69 | 70 | func streamGarblerMode(params *utils.Params, oti ot.OT, input input, 71 | args []string) error { 72 | 73 | inputSizes := make([][]int, 2) 74 | 75 | sizes, err := circuit.InputSizes(input) 76 | if err != nil { 77 | return err 78 | } 79 | inputSizes[0] = sizes 80 | 81 | if len(args) != 1 || !strings.HasSuffix(args[0], ".mpcl") { 82 | return fmt.Errorf("streaming mode takes single MPCL file") 83 | } 84 | nc, err := net.Dial("tcp", port) 85 | if err != nil { 86 | return err 87 | } 88 | conn := p2p.NewConn(nc) 89 | defer conn.Close() 90 | 91 | sizes, err = conn.ReceiveInputSizes() 92 | if err != nil { 93 | return err 94 | } 95 | inputSizes[1] = sizes 96 | 97 | outputs, result, err := compiler.New(params).StreamFile( 98 | conn, oti, args[0], input, inputSizes) 99 | if err != nil { 100 | return err 101 | } 102 | mpc.PrintResults(result, outputs) 103 | return nil 104 | } 105 | -------------------------------------------------------------------------------- /apps/iotest/iotest.go: -------------------------------------------------------------------------------- 1 | // 2 | // main.go 3 | // 4 | // Copyright (c) 2023 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package main 10 | 11 | import ( 12 | "fmt" 13 | "io" 14 | "net" 15 | 16 | "github.com/markkurossi/mpc/circuit" 17 | "github.com/markkurossi/mpc/ot" 18 | "github.com/markkurossi/mpc/p2p" 19 | ) 20 | 21 | func evaluatorTestIO(size int64, once bool) error { 22 | ln, err := net.Listen("tcp", port) 23 | if err != nil { 24 | return err 25 | } 26 | fmt.Printf("Listening for connections at %s\n", port) 27 | 28 | for { 29 | nc, err := ln.Accept() 30 | if err != nil { 31 | return err 32 | } 33 | fmt.Printf("New connection from %s\n", nc.RemoteAddr()) 34 | 35 | conn := p2p.NewConn(nc) 36 | for { 37 | var label ot.Label 38 | var labelData ot.LabelData 39 | err = conn.ReceiveLabel(&label, &labelData) 40 | if err != nil { 41 | if err == io.EOF { 42 | break 43 | } 44 | return err 45 | } 46 | } 47 | fmt.Printf("Received: %v\n", 48 | circuit.FileSize(conn.Stats.Sum()).String()) 49 | 50 | if once { 51 | return nil 52 | } 53 | } 54 | } 55 | 56 | func garblerTestIO(size int64) error { 57 | nc, err := net.Dial("tcp", port) 58 | if err != nil { 59 | return err 60 | } 61 | conn := p2p.NewConn(nc) 62 | 63 | var sent int64 64 | var label ot.Label 65 | var labelData ot.LabelData 66 | 67 | for sent < size { 68 | err = conn.SendLabel(label, &labelData) 69 | if err != nil { 70 | return err 71 | } 72 | sent += int64(len(labelData)) 73 | } 74 | if err := conn.Flush(); err != nil { 75 | return err 76 | } 77 | if err := conn.Close(); err != nil { 78 | return err 79 | } 80 | 81 | fmt.Printf("Sent: %v\n", circuit.FileSize(conn.Stats.Sum()).String()) 82 | return nil 83 | } 84 | -------------------------------------------------------------------------------- /apps/iotest/main.go: -------------------------------------------------------------------------------- 1 | // 2 | // main.go 3 | // 4 | // Copyright (c) 2019-2023 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package main 10 | 11 | import ( 12 | "flag" 13 | "log" 14 | "os" 15 | "runtime/pprof" 16 | ) 17 | 18 | var ( 19 | port = ":8080" 20 | ) 21 | 22 | func main() { 23 | evaluator := flag.Bool("e", false, "evaluator / garbler mode") 24 | cpuprofile := flag.String("cpuprofile", "", "write cpu profile to `file`") 25 | testIO := flag.Int64("test-io", 0, "test I/O performance") 26 | flag.Parse() 27 | 28 | log.SetFlags(0) 29 | 30 | if len(*cpuprofile) > 0 { 31 | f, err := os.Create(*cpuprofile) 32 | if err != nil { 33 | log.Fatal("could not create CPU profile: ", err) 34 | } 35 | defer f.Close() 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 *testIO > 0 { 43 | if *evaluator { 44 | err := evaluatorTestIO(*testIO, len(*cpuprofile) > 0) 45 | if err != nil { 46 | log.Fatal(err) 47 | } 48 | } else { 49 | err := garblerTestIO(*testIO) 50 | if err != nil { 51 | log.Fatal(err) 52 | } 53 | } 54 | return 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /apps/mpcldoc/main.go: -------------------------------------------------------------------------------- 1 | // 2 | // main.go 3 | // 4 | // Copyright (c) 2019-2023 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package main 10 | 11 | import ( 12 | "flag" 13 | "fmt" 14 | "log" 15 | "os" 16 | ) 17 | 18 | func main() { 19 | dir := flag.String("dir", ",apidoc", 20 | "generate documentation to the argument directory") 21 | flag.Parse() 22 | 23 | log.SetFlags(0) 24 | 25 | if len(flag.Args()) == 0 { 26 | fmt.Println("no files specified") 27 | os.Exit(1) 28 | } 29 | 30 | doc, err := NewHTMLDoc(*dir) 31 | if err != nil { 32 | log.Fatal(err) 33 | } 34 | err = documentation(flag.Args(), doc) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /apps/objdump/main.go: -------------------------------------------------------------------------------- 1 | // 2 | // main.go 3 | // 4 | // Copyright (c) 2019-2023 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package main 10 | 11 | import ( 12 | "flag" 13 | "fmt" 14 | "log" 15 | "os" 16 | ) 17 | 18 | func main() { 19 | flag.Parse() 20 | 21 | log.SetFlags(0) 22 | 23 | if len(flag.Args()) == 0 { 24 | fmt.Printf("no files specified\n") 25 | os.Exit(1) 26 | } 27 | if err := dumpObjects(flag.Args()); err != nil { 28 | log.Fatal(err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /apps/objdump/objdump.go: -------------------------------------------------------------------------------- 1 | // 2 | // main.go 3 | // 4 | // Copyright (c) 2019-2022 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package main 10 | 11 | import ( 12 | "os" 13 | 14 | "github.com/markkurossi/mpc/circuit" 15 | "github.com/markkurossi/tabulate" 16 | ) 17 | 18 | func dumpObjects(files []string) error { 19 | type oCircuit struct { 20 | name string 21 | circuit *circuit.Circuit 22 | } 23 | var circuits []oCircuit 24 | 25 | for _, file := range files { 26 | if circuit.IsFilename(file) { 27 | c, err := circuit.Parse(file) 28 | if err != nil { 29 | return err 30 | } 31 | circuits = append(circuits, oCircuit{ 32 | name: file, 33 | circuit: c, 34 | }) 35 | } 36 | } 37 | 38 | if len(circuits) > 0 { 39 | tab := tabulate.New(tabulate.Github) 40 | tab.Header("File") 41 | tab.Header("XOR").SetAlign(tabulate.MR) 42 | tab.Header("XNOR").SetAlign(tabulate.MR) 43 | tab.Header("AND").SetAlign(tabulate.MR) 44 | tab.Header("OR").SetAlign(tabulate.MR) 45 | tab.Header("INV").SetAlign(tabulate.MR) 46 | tab.Header("Gates").SetAlign(tabulate.MR) 47 | tab.Header("xor").SetAlign(tabulate.MR) 48 | tab.Header("!xor").SetAlign(tabulate.MR) 49 | tab.Header("Wires").SetAlign(tabulate.MR) 50 | 51 | for _, c := range circuits { 52 | row := tab.Row() 53 | row.Column(c.name) 54 | c.circuit.TabulateRow(row) 55 | } 56 | 57 | tab.Print(os.Stdout) 58 | } 59 | 60 | return nil 61 | } 62 | -------------------------------------------------------------------------------- /apps/ot/main.go: -------------------------------------------------------------------------------- 1 | // 2 | // main.go 3 | // 4 | // Copyright (c) 2019-2021 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package main 10 | 11 | import ( 12 | "bytes" 13 | "crypto/rand" 14 | "fmt" 15 | "log" 16 | "os" 17 | 18 | "github.com/markkurossi/mpc/ot" 19 | ) 20 | 21 | func main() { 22 | m0, _ := ot.NewLabel(rand.Reader) 23 | m1, _ := ot.NewLabel(rand.Reader) 24 | 25 | sender, err := ot.NewSender(2048) 26 | if err != nil { 27 | log.Fatal(err) 28 | } 29 | fmt.Printf(" Sender m0 : %x\n", m0) 30 | fmt.Printf(" Sender m1 : %x\n", m1) 31 | 32 | receiver, err := ot.NewReceiver(sender.PublicKey()) 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | 37 | var m0Buf, m1Buf ot.LabelData 38 | m0Data := m0.Bytes(&m0Buf) 39 | m1Data := m1.Bytes(&m1Buf) 40 | 41 | sXfer, err := sender.NewTransfer(m0Data, m1Data) 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | rXfer, err := receiver.NewTransfer(0) 46 | if err != nil { 47 | log.Fatal(err) 48 | } 49 | 50 | err = rXfer.ReceiveRandomMessages(sXfer.RandomMessages()) 51 | if err != nil { 52 | log.Fatal(err) 53 | } 54 | 55 | sXfer.ReceiveV(rXfer.V()) 56 | err = rXfer.ReceiveMessages(sXfer.Messages()) 57 | if err != nil { 58 | log.Fatal(err) 59 | } 60 | 61 | m, bit := rXfer.Message() 62 | fmt.Printf("Receiver m%d : %x\n", bit, m) 63 | 64 | var ret int 65 | if bit == 0 { 66 | ret = bytes.Compare(m0Data[:], m) 67 | } else { 68 | ret = bytes.Compare(m1Data[:], m) 69 | } 70 | if ret != 0 { 71 | fmt.Printf("Verify failed!\n") 72 | os.Exit(1) 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /bmr/3party_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022-2024 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package bmr 8 | 9 | import ( 10 | "testing" 11 | 12 | "github.com/markkurossi/mpc/circuit" 13 | "github.com/markkurossi/mpc/ot" 14 | ) 15 | 16 | func Test3Party(t *testing.T) { 17 | circuit, err := circuit.Parse("testdata/3party.mpclc") 18 | if err != nil { 19 | t.Fatalf("could not load circuit: %s", err) 20 | } 21 | 22 | const n = 3 23 | var players []*Player 24 | 25 | for i := 0; i < n; i++ { 26 | p, err := NewPlayer(i, n) 27 | if err != nil { 28 | t.Fatalf("failed to create player: %v", err) 29 | } 30 | err = p.SetCircuit(circuit) 31 | if err != nil { 32 | t.Fatalf("failed to set circuit: %v", err) 33 | } 34 | players = append(players, p) 35 | } 36 | 37 | // Add peers to players. 38 | for i := 0; i < n; i++ { 39 | for j := i + 1; j < n; j++ { 40 | clientFrom, clientTo := ot.NewPipe() 41 | serverFrom, serverTo := ot.NewPipe() 42 | 43 | players[i].AddPeer(j, clientFrom, serverTo) 44 | players[j].AddPeer(i, serverFrom, clientTo) 45 | } 46 | } 47 | 48 | // Start other peers. 49 | for i := 1; i < n; i++ { 50 | go players[i].Play() 51 | } 52 | 53 | // Play player 0. 54 | players[0].Verbose = true 55 | err = players[0].Play() 56 | if err != nil { 57 | t.Fatalf("Play: %v", err) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /bmr/README.md: -------------------------------------------------------------------------------- 1 | # Abstract 2 | 3 | This module implements the [Optimizing Semi-Honest Secure Multiparty 4 | Computation for the Internet](https://eprint.iacr.org/2016/1066) 5 | multi-party protocol. 6 | 7 | # BMR Protocol 8 | 9 | 10 | ```ascii 11 | k_{u}0 +-----\ 12 | k_{u}1 --u--+ \ 13 | | f +--w-- k_{w}0 14 | k_{v}0 --v--+ / k_{w}1 15 | k_{v}1 +-----/ 16 | ``` 17 | 18 | | u | v | w | 19 | |:------:|:------:|:------:| 20 | | k_{u}0 | k_{v}0 | k_{w}0 | 21 | | k_{u}0 | k_{v}1 | k_{w}0 | 22 | | k_{u}1 | k_{v}0 | k_{w}0 | 23 | | k_{u}1 | k_{v}1 | k_{w}1 | 24 | 25 | 26 | In particular, all n parties choose their own random 0-labels and 27 | random 1-labels on every wire, and the output wire labels are 28 | encrypted separately under every single party’s input wire labels 29 | 30 | Let `k^{i}_{u,b}` input-label that party P_i holds for value b{0,1} on 31 | wire u. 32 | 33 | Encryption 34 | 35 | ``` ascii 36 | for g := 0...numGates { 37 | for j := 1...n { 38 | for i := 1...n { 39 | input ^= F2(k^i_{u,a}, k^i_{u,b}, g|j) 40 | } 41 | P_{j} = input|k^j_{w,g(a,b)} 42 | } 43 | } 44 | ``` 45 | 46 | Offline phase: each party P_{i} locally computes: 47 | 48 | ``` ascii 49 | F2(k^i_{u,a}, k^i_{v,b}, g|j) 50 | ``` 51 | 52 | for every a,b in {0,1} and j in [1...n] 53 | 54 | Observe that this means that each party must carry out 4n double-key 55 | PRF computations per gate. 56 | -------------------------------------------------------------------------------- /bmr/fx.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package bmr 8 | 9 | import ( 10 | "github.com/markkurossi/mpc/ot" 11 | ) 12 | 13 | // FxSend implements the sender part of the secure multiplication 14 | // algorithm Fx (3.1.1 Secure Multiplication - page 7). 15 | func FxSend(oti ot.OT, a uint) (r uint, err error) { 16 | rl, err := NewLabel() 17 | if err != nil { 18 | return 0, err 19 | } 20 | 21 | x0 := rl 22 | x1 := rl 23 | 24 | var al Label 25 | al[0] = byte(a) 26 | x1.Xor(al) 27 | 28 | wire := ot.Wire{ 29 | L0: x0.ToOT(), 30 | L1: x1.ToOT(), 31 | } 32 | 33 | err = oti.Send([]ot.Wire{wire}) 34 | if err != nil { 35 | return 0, err 36 | } 37 | return uint(rl[0] & 1), nil 38 | } 39 | 40 | // FxReceive implements the receiver part of the secure multiplication 41 | // algorithm Fx (3.1.1 Secure Multiplication - page 7). 42 | func FxReceive(oti ot.OT, b uint) (xb uint, err error) { 43 | flags := []bool{b == 1} 44 | var result [1]ot.Label 45 | 46 | err = oti.Receive(flags, result[:]) 47 | if err != nil { 48 | return 0, err 49 | } 50 | var xl Label 51 | xl.FromOT(result[0]) 52 | return uint(xl[0] & 1), nil 53 | } 54 | 55 | // FxkSend implements the sender part of the secure multiplication 56 | // algorithm Fx (3.1.1 Secure Multiplication - page 7). 57 | func FxkSend(oti ot.OT, s Label) (r Label, err error) { 58 | r, err = NewLabel() 59 | if err != nil { 60 | return 61 | } 62 | 63 | x0 := r 64 | x1 := r 65 | x1.Xor(s) 66 | 67 | wire := ot.Wire{ 68 | L0: x0.ToOT(), 69 | L1: x1.ToOT(), 70 | } 71 | 72 | err = oti.Send([]ot.Wire{wire}) 73 | return 74 | } 75 | 76 | // FxkReceive implements the receiver part of the secure multiplication 77 | // algorithm Fx (3.1.1 Secure Multiplication - page 7). 78 | func FxkReceive(oti ot.OT, b uint) (xb Label, err error) { 79 | flags := []bool{b == 1} 80 | var result [1]ot.Label 81 | 82 | err = oti.Receive(flags, result[:]) 83 | if err != nil { 84 | return 85 | } 86 | xb.FromOT(result[0]) 87 | return 88 | } 89 | -------------------------------------------------------------------------------- /bmr/network.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package bmr 8 | 9 | // Operand defines protocol operands. 10 | // 11 | //go:generate stringer -type=Operand -trimprefix=Op 12 | type Operand byte 13 | 14 | // Network protocol messages. 15 | const ( 16 | OpInit Operand = iota 17 | OpFxLambda 18 | OpFxR 19 | ) 20 | -------------------------------------------------------------------------------- /bmr/operand_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=Operand -trimprefix=Op"; DO NOT EDIT. 2 | 3 | package bmr 4 | 5 | import "strconv" 6 | 7 | func _() { 8 | // An "invalid array index" compiler error signifies that the constant values have changed. 9 | // Re-run the stringer command to generate them again. 10 | var x [1]struct{} 11 | _ = x[OpInit-0] 12 | _ = x[OpFxLambda-1] 13 | _ = x[OpFxR-2] 14 | } 15 | 16 | const _Operand_name = "InitFxLambdaFxR" 17 | 18 | var _Operand_index = [...]uint8{0, 4, 12, 15} 19 | 20 | func (i Operand) String() string { 21 | if i >= Operand(len(_Operand_index)-1) { 22 | return "Operand(" + strconv.FormatInt(int64(i), 10) + ")" 23 | } 24 | return _Operand_name[_Operand_index[i]:_Operand_index[i+1]] 25 | } 26 | -------------------------------------------------------------------------------- /bmr/testdata/3party.circ.svg: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | i0 10 | 11 | 12 | i1 13 | 14 | 15 | i2 16 | 17 | 18 | 24 | 27 | 30 | 33 | 34 | 35 | 41 | 44 | 47 | 50 | 51 | 52 | o0 53 | 54 | 58 | 62 | 66 | 70 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /bmr/testdata/3party.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | func main(a, b, c uint1) uint { 6 | return a & b & c 7 | } 8 | -------------------------------------------------------------------------------- /bmr/testdata/3party.mpclc: -------------------------------------------------------------------------------- 1 | crca{1,0}u1uint1b{1,0}u1uint1c{1,0}u1uint1 %ret0{1,1}u1uint1 -------------------------------------------------------------------------------- /bmr/wire.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022-2024 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package bmr 8 | 9 | import ( 10 | "bytes" 11 | "crypto/rand" 12 | "encoding/binary" 13 | "fmt" 14 | 15 | "github.com/markkurossi/mpc/ot" 16 | ) 17 | 18 | // Wire implements a circuit wire. 19 | type Wire struct { 20 | L0 Label 21 | L1 Label 22 | } 23 | 24 | func (w Wire) String() string { 25 | return fmt.Sprintf("%v|%v", w.L0, w.L1) 26 | } 27 | 28 | // Label implements a wire 0 or 1 label. 29 | type Label [k / 8]byte 30 | 31 | // NewLabel creates a new random label. 32 | func NewLabel() (Label, error) { 33 | var l Label 34 | 35 | _, err := rand.Read(l[:]) 36 | if err != nil { 37 | return l, err 38 | } 39 | return l, nil 40 | } 41 | 42 | func (l Label) String() string { 43 | return fmt.Sprintf("%x", [k / 8]byte(l)) 44 | } 45 | 46 | // Equal tests if the label is equal with the argument label. 47 | func (l Label) Equal(o Label) bool { 48 | return bytes.Equal(l[:], o[:]) 49 | } 50 | 51 | // Xor sets l to l^o. 52 | func (l *Label) Xor(o Label) { 53 | for i := 0; i < len(l); i++ { 54 | l[i] ^= o[i] 55 | } 56 | } 57 | 58 | // Mul multiplies l with the bit b. 59 | func (l *Label) Mul(b uint) { 60 | for i := 0; i < len(l); i++ { 61 | l[i] *= byte(b) 62 | } 63 | } 64 | 65 | // ToOT converts the label to ot.Label. 66 | func (l *Label) ToOT() ot.Label { 67 | var label ot.Label 68 | label.D0 = uint64(binary.BigEndian.Uint32(l[:])) 69 | return label 70 | } 71 | 72 | // FromOT sets the label to the ot.Label. 73 | func (l *Label) FromOT(label ot.Label) { 74 | binary.BigEndian.PutUint32(l[:], uint32(label.D0)) 75 | } 76 | -------------------------------------------------------------------------------- /circuit/aesni/.gitignore: -------------------------------------------------------------------------------- 1 | c/aesni 2 | -------------------------------------------------------------------------------- /circuit/aesni/GNUmakefile: -------------------------------------------------------------------------------- 1 | 2 | all: benchmarks 3 | 4 | benchmarks: c/aesni 5 | go test -bench=. 6 | c/aesni 7 | 8 | c/aesni: c/aesni.c 9 | gcc -Wall -maes -march=native -o $@ $+ 10 | -------------------------------------------------------------------------------- /circuit/analyze.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package circuit 8 | 9 | import ( 10 | "fmt" 11 | ) 12 | 13 | // Analyze identifies potential optimizations for the circuit. 14 | func (c *Circuit) Analyze() { 15 | fmt.Printf("analyzing circuit %v\n", c) 16 | 17 | from := make([][]Gate, c.NumWires) 18 | to := make([][]Gate, c.NumWires) 19 | 20 | // Collect wire inputs and outputs. 21 | for _, g := range c.Gates { 22 | switch g.Op { 23 | case XOR, XNOR, AND, OR: 24 | to[g.Input1] = append(to[g.Input1], g) 25 | fallthrough 26 | 27 | case INV: 28 | to[g.Input0] = append(to[g.Input0], g) 29 | from[g.Output] = append(to[g.Output], g) 30 | } 31 | } 32 | 33 | // INV gates as single output of input gate. 34 | for _, g := range c.Gates { 35 | if g.Op != INV { 36 | continue 37 | } 38 | if len(to[g.Input0]) == 1 && len(from[g.Input0]) == 1 { 39 | fmt.Printf("%v -> %v\n", from[g.Input0][0].Op, g.Op) 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /circuit/circuit_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022-2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package circuit 8 | 9 | import ( 10 | "testing" 11 | "unsafe" 12 | ) 13 | 14 | func TestSize(t *testing.T) { 15 | var g Gate 16 | if unsafe.Sizeof(g) != 20 { 17 | t.Errorf("unexpected gate size: got %v, expected 20", unsafe.Sizeof(g)) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /circuit/computer.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package circuit 8 | 9 | import ( 10 | "fmt" 11 | "math/big" 12 | ) 13 | 14 | // Compute evaluates the circuit with the given input values. 15 | func (c *Circuit) Compute(inputs []*big.Int) ([]*big.Int, error) { 16 | // Flatten circuit arguments. 17 | var args IO 18 | for _, io := range c.Inputs { 19 | if len(io.Compound) > 0 { 20 | args = append(args, io.Compound...) 21 | } else { 22 | args = append(args, io) 23 | } 24 | } 25 | if len(inputs) != len(args) { 26 | return nil, fmt.Errorf("invalid inputs: got %d, expected %d", 27 | len(inputs), len(args)) 28 | } 29 | 30 | // Flatten inputs and arguments. 31 | wires := make([]byte, c.NumWires) 32 | 33 | var w int 34 | for idx, io := range args { 35 | for bit := 0; bit < int(io.Type.Bits); bit++ { 36 | wires[w] = byte(inputs[idx].Bit(bit)) 37 | w++ 38 | } 39 | } 40 | 41 | // Evaluate circuit. 42 | for _, gate := range c.Gates { 43 | var result byte 44 | 45 | switch gate.Op { 46 | case XOR: 47 | result = wires[gate.Input0] ^ wires[gate.Input1] 48 | 49 | case XNOR: 50 | if wires[gate.Input0]^wires[gate.Input1] == 0 { 51 | result = 1 52 | } else { 53 | result = 0 54 | } 55 | 56 | case AND: 57 | result = wires[gate.Input0] & wires[gate.Input1] 58 | 59 | case OR: 60 | result = wires[gate.Input0] | wires[gate.Input1] 61 | 62 | case INV: 63 | if wires[gate.Input0] == 0 { 64 | result = 1 65 | } else { 66 | result = 0 67 | } 68 | 69 | default: 70 | return nil, fmt.Errorf("invalid gate %s", gate.Op) 71 | } 72 | 73 | wires[gate.Output] = result 74 | } 75 | 76 | // Construct outputs 77 | w = c.NumWires - c.Outputs.Size() 78 | var result []*big.Int 79 | for _, io := range c.Outputs { 80 | r := new(big.Int) 81 | for bit := 0; bit < int(io.Type.Bits); bit++ { 82 | if wires[w] != 0 { 83 | r.SetBit(r, bit, 1) 84 | } 85 | w++ 86 | } 87 | result = append(result, r) 88 | } 89 | 90 | return result, nil 91 | } 92 | -------------------------------------------------------------------------------- /circuit/docs/and.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 9 | 12 | 15 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /circuit/docs/not.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 13 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /circuit/docs/or.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 8 | 11 | 14 | 17 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /circuit/docs/wire.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /circuit/docs/xnor.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 10 | 13 | 16 | 19 | 22 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /circuit/docs/xor.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 10 | 13 | 16 | 19 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /circuit/dot.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package circuit 8 | 9 | import ( 10 | "fmt" 11 | "io" 12 | ) 13 | 14 | // Dot creates graphviz dot output of the circuit. 15 | func (c *Circuit) Dot(out io.Writer) { 16 | fmt.Fprintf(out, "digraph circuit\n{\n") 17 | fmt.Fprintf(out, " overlap=scale;\n") 18 | fmt.Fprintf(out, " node\t[fontname=\"Helvetica\"];\n") 19 | fmt.Fprintf(out, " {\n node [shape=plaintext];\n") 20 | for w := 0; w < c.NumWires; w++ { 21 | fmt.Fprintf(out, " w%d\t[label=\"%d\"];\n", w, w) 22 | } 23 | fmt.Fprintf(out, " }\n") 24 | 25 | fmt.Fprintf(out, " {\n node [shape=box];\n") 26 | for idx, gate := range c.Gates { 27 | fmt.Fprintf(out, " g%d\t[label=\"%s\"];\n", idx, gate.Op) 28 | } 29 | fmt.Fprintf(out, " }\n") 30 | 31 | if true { 32 | fmt.Fprintf(out, " { rank=same") 33 | var numInputs int 34 | for _, input := range c.Inputs { 35 | numInputs += int(input.Type.Bits) 36 | } 37 | for w := 0; w < numInputs; w++ { 38 | fmt.Fprintf(out, "; w%d", w) 39 | } 40 | fmt.Fprintf(out, ";}\n") 41 | 42 | fmt.Fprintf(out, " { rank=same") 43 | for w := 0; w < c.Outputs.Size(); w++ { 44 | fmt.Fprintf(out, "; w%d", c.NumWires-w-1) 45 | } 46 | fmt.Fprintf(out, ";}\n") 47 | } 48 | 49 | for idx, gate := range c.Gates { 50 | for _, i := range gate.Inputs() { 51 | fmt.Fprintf(out, " w%d -> g%d;\n", i, idx) 52 | } 53 | fmt.Fprintf(out, " g%d -> w%d;\n", idx, gate.Output) 54 | } 55 | fmt.Fprintf(out, "}\n") 56 | } 57 | -------------------------------------------------------------------------------- /circuit/enc_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // enc_test.go 3 | // 4 | // Copyright (c) 2019-2023 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package circuit 10 | 11 | import ( 12 | "crypto/aes" 13 | "crypto/rand" 14 | "testing" 15 | 16 | "github.com/markkurossi/mpc/ot" 17 | ) 18 | 19 | func TestEnc(t *testing.T) { 20 | a, _ := ot.NewLabel(rand.Reader) 21 | b, _ := ot.NewLabel(rand.Reader) 22 | c, _ := ot.NewLabel(rand.Reader) 23 | tweak := uint32(42) 24 | var key [32]byte 25 | 26 | cipher, err := aes.NewCipher(key[:]) 27 | if err != nil { 28 | t.Fatalf("Failed to create cipher: %s", err) 29 | } 30 | 31 | var data ot.LabelData 32 | 33 | encrypted := encrypt(cipher, a, b, c, tweak, &data) 34 | if err != nil { 35 | t.Fatalf("Encrypt failed: %s", err) 36 | } 37 | 38 | plain := decrypt(cipher, a, b, tweak, encrypted, &data) 39 | 40 | if !c.Equal(plain) { 41 | t.Fatalf("Encrypt-decrypt failed") 42 | } 43 | } 44 | 45 | func BenchmarkEnc(b *testing.B) { 46 | var key [32]byte 47 | 48 | cipher, err := aes.NewCipher(key[:]) 49 | if err != nil { 50 | b.Fatalf("Failed to create cipher: %s", err) 51 | } 52 | 53 | al, err := ot.NewLabel(rand.Reader) 54 | if err != nil { 55 | b.Fatalf("Failed to create label: %s", err) 56 | } 57 | bl, err := ot.NewLabel(rand.Reader) 58 | if err != nil { 59 | b.Fatalf("Failed to create label: %s", err) 60 | } 61 | cl, err := ot.NewLabel(rand.Reader) 62 | if err != nil { 63 | b.Fatalf("Failed to create label: %s", err) 64 | } 65 | 66 | b.ResetTimer() 67 | var data ot.LabelData 68 | for i := 0; i < b.N; i++ { 69 | encrypt(cipher, al, bl, cl, uint32(i), &data) 70 | } 71 | } 72 | 73 | func BenchmarkEncHalf(b *testing.B) { 74 | var key [32]byte 75 | 76 | cipher, err := aes.NewCipher(key[:]) 77 | if err != nil { 78 | b.Fatalf("Failed to create cipher: %s", err) 79 | } 80 | 81 | xl, err := ot.NewLabel(rand.Reader) 82 | if err != nil { 83 | b.Fatalf("Failed to create label: %s", err) 84 | } 85 | 86 | b.ResetTimer() 87 | var data ot.LabelData 88 | for i := 0; i < b.N; i++ { 89 | encryptHalf(cipher, xl, uint32(i), &data) 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /circuit/eval.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019-2021 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package circuit 8 | 9 | import ( 10 | "crypto/aes" 11 | "fmt" 12 | 13 | "github.com/markkurossi/mpc/ot" 14 | ) 15 | 16 | // Eval evaluates the circuit. 17 | func (c *Circuit) Eval(key []byte, wires []ot.Label, 18 | garbled [][]ot.Label) error { 19 | 20 | alg, err := aes.NewCipher(key) 21 | if err != nil { 22 | return err 23 | } 24 | 25 | var data ot.LabelData 26 | var id uint32 27 | 28 | for i := 0; i < len(c.Gates); i++ { 29 | gate := &c.Gates[i] 30 | 31 | var a, b, c ot.Label 32 | 33 | switch gate.Op { 34 | case XOR, XNOR, AND, OR: 35 | a = wires[gate.Input0] 36 | b = wires[gate.Input1] 37 | 38 | case INV: 39 | a = wires[gate.Input0] 40 | 41 | default: 42 | return fmt.Errorf("invalid operation %s", gate.Op) 43 | } 44 | 45 | var output ot.Label 46 | 47 | switch gate.Op { 48 | case XOR, XNOR: 49 | a.Xor(b) 50 | output = a 51 | 52 | case AND: 53 | row := garbled[i] 54 | if len(row) != 2 { 55 | return fmt.Errorf("corrupted ciruit: AND row length: %d", 56 | len(row)) 57 | } 58 | sa := a.S() 59 | sb := b.S() 60 | 61 | j0 := id 62 | j1 := id + 1 63 | id += 2 64 | 65 | tg := row[0] 66 | te := row[1] 67 | 68 | wg := encryptHalf(alg, a, j0, &data) 69 | if sa { 70 | wg.Xor(tg) 71 | } 72 | we := encryptHalf(alg, b, j1, &data) 73 | if sb { 74 | we.Xor(te) 75 | we.Xor(a) 76 | } 77 | output = wg 78 | output.Xor(we) 79 | 80 | case OR: 81 | row := garbled[i] 82 | index := idx(a, b) 83 | if index > 0 { 84 | // First row is zero and not transmitted. 85 | index-- 86 | if index >= len(row) { 87 | return fmt.Errorf("corrupted circuit: index %d >= row %d", 88 | index, len(row)) 89 | } 90 | c = row[index] 91 | } 92 | 93 | output = decrypt(alg, a, b, id, c, &data) 94 | id++ 95 | 96 | case INV: 97 | row := garbled[i] 98 | index := idxUnary(a) 99 | if index > 0 { 100 | // First row is zero and not transmitted. 101 | index-- 102 | if index >= len(row) { 103 | return fmt.Errorf("corrupted circuit: index %d >= row %d", 104 | index, len(row)) 105 | } 106 | c = row[index] 107 | } 108 | output = decrypt(alg, a, ot.Label{}, id, c, &data) 109 | id++ 110 | } 111 | wires[gate.Output] = output 112 | } 113 | 114 | return nil 115 | } 116 | -------------------------------------------------------------------------------- /circuit/ioarg_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package circuit 8 | 9 | import ( 10 | "testing" 11 | ) 12 | 13 | var inputSizeTests = []struct { 14 | inputs []string 15 | sizes []int 16 | }{ 17 | { 18 | inputs: []string{ 19 | "0", "f", "false", "1", "t", "true", 20 | }, 21 | sizes: []int{ 22 | 1, 1, 1, 1, 1, 1, 23 | }, 24 | }, 25 | { 26 | inputs: []string{ 27 | "0xdeadbeef", "255", 28 | }, 29 | sizes: []int{ 30 | 32, 8, 31 | }, 32 | }, 33 | { 34 | inputs: []string{ 35 | "0x0", "0x00", "0x000", "0x0000", 36 | }, 37 | sizes: []int{ 38 | 4, 8, 12, 16, 39 | }, 40 | }, 41 | } 42 | 43 | func TestInputSizes(t *testing.T) { 44 | for idx, test := range inputSizeTests { 45 | sizes, err := InputSizes(test.inputs) 46 | if err != nil { 47 | t.Errorf("t%v: InputSizes(%v) failed: %v", idx, test.inputs, err) 48 | continue 49 | } 50 | if len(sizes) != len(test.sizes) { 51 | t.Errorf("t%v: unexpected # of sizes: got %v, expected %v", 52 | idx, len(sizes), len(test.sizes)) 53 | continue 54 | } 55 | for i := 0; i < len(sizes); i++ { 56 | if sizes[i] != test.sizes[i] { 57 | t.Errorf("t%v: sizes[%v]=%v, expected %v", 58 | idx, i, sizes[i], test.sizes[i]) 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /circuit/parser_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // parser_test.go 3 | // 4 | // Copyright (c) 2019-2023 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package circuit 10 | 11 | import ( 12 | "bytes" 13 | "testing" 14 | ) 15 | 16 | var data = `1 3 17 | 2 1 1 18 | 1 1 19 | 20 | 2 1 0 1 2 AND 21 | ` 22 | 23 | func TestParse(t *testing.T) { 24 | _, err := ParseBristol(bytes.NewReader([]byte(data))) 25 | if err != nil { 26 | t.Fatalf("Parse failed: %s", err) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /circuit/template_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2022-2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package circuit 8 | 9 | import ( 10 | "testing" 11 | ) 12 | 13 | var tmplXOR = ` 14 | 17 | 20 | 21 | 25 | 29 | 30 | 31 | 35 | 39 | 43 | 44 | ` 45 | 46 | var tmplXORExpanded = ` 47 | 50 | 53 | 54 | 58 | 62 | 63 | 64 | 68 | 72 | 76 | 77 | ` 78 | 79 | func TestTemplate(t *testing.T) { 80 | tmpl := NewTemplate(tmplXOR) 81 | tmpl.IntCvt = func(v int) float64 { 82 | return float64(v) * 25 / 100 83 | } 84 | expanded := tmpl.Expand() 85 | if expanded != tmplXORExpanded { 86 | t.Errorf("template expansion failed: got\n%v\n", expanded) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /compiler/.gitignore: -------------------------------------------------------------------------------- 1 | *.circ 2 | -------------------------------------------------------------------------------- /compiler/circuits/allocator.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package circuits 8 | 9 | import ( 10 | "fmt" 11 | "unsafe" 12 | 13 | "github.com/markkurossi/mpc/circuit" 14 | "github.com/markkurossi/mpc/types" 15 | ) 16 | 17 | var ( 18 | sizeofWire = uint64(unsafe.Sizeof(Wire{})) 19 | sizeofGate = uint64(unsafe.Sizeof(Gate{})) 20 | ) 21 | 22 | // Allocator implements circuit wire and gate allocation. 23 | type Allocator struct { 24 | numWire uint64 25 | numWires uint64 26 | numGates uint64 27 | } 28 | 29 | // NewAllocator creates a new circuit allocator. 30 | func NewAllocator() *Allocator { 31 | return new(Allocator) 32 | } 33 | 34 | // Wire allocates a new Wire. 35 | func (alloc *Allocator) Wire() *Wire { 36 | alloc.numWire++ 37 | w := new(Wire) 38 | w.Reset(UnassignedID) 39 | return w 40 | } 41 | 42 | // Wires allocate an array of Wires. 43 | func (alloc *Allocator) Wires(bits types.Size) []*Wire { 44 | alloc.numWires += uint64(bits) 45 | 46 | wires := make([]Wire, bits) 47 | result := make([]*Wire, bits) 48 | for i := 0; i < int(bits); i++ { 49 | w := &wires[i] 50 | w.id = UnassignedID 51 | result[i] = w 52 | } 53 | return result 54 | } 55 | 56 | // BinaryGate creates a new binary gate. 57 | func (alloc *Allocator) BinaryGate(op circuit.Operation, a, b, o *Wire) *Gate { 58 | alloc.numGates++ 59 | gate := &Gate{ 60 | Op: op, 61 | A: a, 62 | B: b, 63 | O: o, 64 | } 65 | a.AddOutput(gate) 66 | b.AddOutput(gate) 67 | o.SetInput(gate) 68 | 69 | return gate 70 | } 71 | 72 | // INVGate creates a new INV gate. 73 | func (alloc *Allocator) INVGate(i, o *Wire) *Gate { 74 | alloc.numGates++ 75 | gate := &Gate{ 76 | Op: circuit.INV, 77 | A: i, 78 | O: o, 79 | } 80 | i.AddOutput(gate) 81 | o.SetInput(gate) 82 | 83 | return gate 84 | } 85 | 86 | // Debug print debugging information about the circuit allocator. 87 | func (alloc *Allocator) Debug() { 88 | wireSize := circuit.FileSize(alloc.numWire * sizeofWire) 89 | wiresSize := circuit.FileSize(alloc.numWires * sizeofWire) 90 | gatesSize := circuit.FileSize(alloc.numGates * sizeofGate) 91 | 92 | total := float64(wireSize + wiresSize + gatesSize) 93 | 94 | fmt.Println("circuits.Allocator:") 95 | fmt.Printf(" wire : %9v %5s %5.2f%%\n", 96 | alloc.numWire, wireSize, float64(wireSize)/total*100.0) 97 | fmt.Printf(" wires: %9v %5s %5.2f%%\n", 98 | alloc.numWires, wiresSize, float64(wiresSize)/total*100.0) 99 | fmt.Printf(" gates: %9v %5s %5.2f%%\n", 100 | alloc.numGates, gatesSize, float64(gatesSize)/total*100.0) 101 | } 102 | -------------------------------------------------------------------------------- /compiler/circuits/circ_adder.go: -------------------------------------------------------------------------------- 1 | // 2 | // circ_adder.go 3 | // 4 | // Copyright (c) 2019-2023 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package circuits 10 | 11 | import ( 12 | "github.com/markkurossi/mpc/circuit" 13 | ) 14 | 15 | // NewHalfAdder creates a half adder circuit. 16 | func NewHalfAdder(cc *Compiler, a, b, s, c *Wire) { 17 | // S = XOR(A, B) 18 | cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, a, b, s)) 19 | 20 | if c != nil { 21 | // C = AND(A, B) 22 | cc.AddGate(cc.Calloc.BinaryGate(circuit.AND, a, b, c)) 23 | } 24 | } 25 | 26 | // NewFullAdder creates a full adder circuit 27 | func NewFullAdder(cc *Compiler, a, b, cin, s, cout *Wire) { 28 | w1 := cc.Calloc.Wire() 29 | w2 := cc.Calloc.Wire() 30 | w3 := cc.Calloc.Wire() 31 | 32 | // s = a XOR b XOR cin 33 | // cout = cin XOR ((a XOR cin) AND (b XOR cin)). 34 | 35 | // w1 = XOR(b, cin) 36 | cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, b, cin, w1)) 37 | 38 | // s = XOR(a, w1) 39 | cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, a, w1, s)) 40 | 41 | if cout != nil { 42 | // w2 = XOR(a, cin) 43 | cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, a, cin, w2)) 44 | 45 | // w3 = AND(w1, w2) 46 | cc.AddGate(cc.Calloc.BinaryGate(circuit.AND, w1, w2, w3)) 47 | 48 | // cout = XOR(cin, w3) 49 | cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, cin, w3, cout)) 50 | } 51 | } 52 | 53 | // NewAdder creates a new adder circuit implementing z=x+y. 54 | func NewAdder(cc *Compiler, x, y, z []*Wire) error { 55 | x, y = cc.ZeroPad(x, y) 56 | if len(x) > len(z) { 57 | x = x[0:len(z)] 58 | y = y[0:len(z)] 59 | } 60 | 61 | if len(x) == 1 { 62 | var cin *Wire 63 | if len(z) > 1 { 64 | cin = z[1] 65 | } 66 | NewHalfAdder(cc, x[0], y[0], z[0], cin) 67 | } else { 68 | cin := cc.Calloc.Wire() 69 | NewHalfAdder(cc, x[0], y[0], z[0], cin) 70 | 71 | for i := 1; i < len(x); i++ { 72 | var cout *Wire 73 | if i+1 >= len(x) { 74 | if i+1 >= len(z) { 75 | // N+N=N, overflow, drop carry bit. 76 | cout = nil 77 | } else { 78 | cout = z[len(x)] 79 | } 80 | } else { 81 | cout = cc.Calloc.Wire() 82 | } 83 | 84 | NewFullAdder(cc, x[i], y[i], cin, z[i], cout) 85 | 86 | cin = cout 87 | } 88 | } 89 | 90 | // Set all leftover bits to zero. 91 | for i := len(x) + 1; i < len(z); i++ { 92 | z[i] = cc.ZeroWire() 93 | } 94 | 95 | return nil 96 | } 97 | -------------------------------------------------------------------------------- /compiler/circuits/circ_binary.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2021, 2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package circuits 8 | 9 | import ( 10 | "github.com/markkurossi/mpc/circuit" 11 | ) 12 | 13 | // NewBinaryAND creates a new binary AND circuit implementing r=x&y 14 | func NewBinaryAND(cc *Compiler, x, y, r []*Wire) error { 15 | x, y = cc.ZeroPad(x, y) 16 | if len(r) < len(x) { 17 | x = x[0:len(r)] 18 | y = y[0:len(r)] 19 | } 20 | for i := 0; i < len(x); i++ { 21 | cc.AddGate(cc.Calloc.BinaryGate(circuit.AND, x[i], y[i], r[i])) 22 | } 23 | return nil 24 | } 25 | 26 | // NewBinaryClear creates a new binary clear circuit implementing r=x&^y. 27 | func NewBinaryClear(cc *Compiler, x, y, r []*Wire) error { 28 | x, y = cc.ZeroPad(x, y) 29 | if len(r) < len(x) { 30 | x = x[0:len(r)] 31 | y = y[0:len(r)] 32 | } 33 | for i := 0; i < len(x); i++ { 34 | w := cc.Calloc.Wire() 35 | cc.INV(y[i], w) 36 | cc.AddGate(cc.Calloc.BinaryGate(circuit.AND, x[i], w, r[i])) 37 | } 38 | return nil 39 | } 40 | 41 | // NewBinaryOR creates a new binary OR circuit implementing r=x|y. 42 | func NewBinaryOR(cc *Compiler, x, y, r []*Wire) error { 43 | x, y = cc.ZeroPad(x, y) 44 | if len(r) < len(x) { 45 | x = x[0:len(r)] 46 | y = y[0:len(r)] 47 | } 48 | for i := 0; i < len(x); i++ { 49 | cc.AddGate(cc.Calloc.BinaryGate(circuit.OR, x[i], y[i], r[i])) 50 | } 51 | return nil 52 | } 53 | 54 | // NewBinaryXOR creates a new binary XOR circuit implementing r=x^y. 55 | func NewBinaryXOR(cc *Compiler, x, y, r []*Wire) error { 56 | x, y = cc.ZeroPad(x, y) 57 | if len(r) < len(x) { 58 | x = x[0:len(r)] 59 | y = y[0:len(r)] 60 | } 61 | for i := 0; i < len(x); i++ { 62 | cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, x[i], y[i], r[i])) 63 | } 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /compiler/circuits/circ_hamming.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package circuits 8 | 9 | import ( 10 | "github.com/markkurossi/mpc/circuit" 11 | "github.com/markkurossi/mpc/types" 12 | ) 13 | 14 | // Hamming creates a hamming distance circuit computing the hamming 15 | // distance between a and b and returning the distance in r. 16 | func Hamming(cc *Compiler, a, b, r []*Wire) error { 17 | a, b = cc.ZeroPad(a, b) 18 | 19 | var arr [][]*Wire 20 | for i := 0; i < len(a); i++ { 21 | w := cc.Calloc.Wire() 22 | cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, a[i], b[i], w)) 23 | arr = append(arr, []*Wire{w}) 24 | } 25 | 26 | for len(arr) > 2 { 27 | var n [][]*Wire 28 | for i := 0; i < len(arr); i += 2 { 29 | if i+1 < len(arr) { 30 | result := cc.Calloc.Wires(types.Size(len(arr[i]) + 1)) 31 | err := NewAdder(cc, arr[i], arr[i+1], result) 32 | if err != nil { 33 | return err 34 | } 35 | n = append(n, result) 36 | } else { 37 | n = append(n, arr[i]) 38 | } 39 | } 40 | arr = n 41 | } 42 | 43 | return NewAdder(cc, arr[0], arr[1], r) 44 | } 45 | -------------------------------------------------------------------------------- /compiler/circuits/circ_index.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021-2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package circuits 8 | 9 | import ( 10 | "fmt" 11 | ) 12 | 13 | // NewIndex creates a new array element selection (index) circuit. 14 | func NewIndex(cc *Compiler, size int, array, index, out []*Wire) error { 15 | if len(array)%size != 0 { 16 | return fmt.Errorf("array width %d must be multiple of element size %d", 17 | len(array), size) 18 | } 19 | if len(out) < size { 20 | return fmt.Errorf("out %d too small for element size %d", 21 | len(out), size) 22 | } 23 | n := len(array) / size 24 | if n == 0 { 25 | for i := 0; i < len(out); i++ { 26 | out[i] = cc.ZeroWire() 27 | } 28 | return nil 29 | } 30 | 31 | bits := 1 32 | var length int 33 | 34 | for length = 2; length < n; length *= 2 { 35 | bits++ 36 | } 37 | 38 | return newIndex(cc, bits-1, length, size, array, index, out) 39 | } 40 | 41 | func newIndex(cc *Compiler, bit, length, size int, 42 | array, index, out []*Wire) error { 43 | 44 | // Default "not found" value. 45 | def := make([]*Wire, size) 46 | for i := 0; i < size; i++ { 47 | def[i] = cc.ZeroWire() 48 | } 49 | 50 | n := len(array) / size 51 | 52 | if bit == 0 { 53 | fVal := array[:size] 54 | 55 | var tVal []*Wire 56 | if n > 1 { 57 | tVal = array[size : 2*size] 58 | } else { 59 | tVal = def 60 | } 61 | return NewMUX(cc, index[0:1], tVal, fVal, out) 62 | } 63 | 64 | length /= 2 65 | fArray := array 66 | if n > length { 67 | fArray = fArray[:length*size] 68 | } 69 | 70 | if bit >= len(index) { 71 | // Not enough bits to select upper half so just select from 72 | // the lower half. 73 | return newIndex(cc, bit-1, length, size, fArray, index, out) 74 | } 75 | 76 | fVal := make([]*Wire, size) 77 | for i := 0; i < size; i++ { 78 | fVal[i] = cc.Calloc.Wire() 79 | } 80 | err := newIndex(cc, bit-1, length, size, fArray, index, fVal) 81 | if err != nil { 82 | return err 83 | } 84 | 85 | var tVal []*Wire 86 | if n > length { 87 | tVal = make([]*Wire, size) 88 | for i := 0; i < size; i++ { 89 | tVal[i] = cc.Calloc.Wire() 90 | } 91 | err = newIndex(cc, bit-1, length, size, 92 | array[length*size:], index, tVal) 93 | if err != nil { 94 | return err 95 | } 96 | } else { 97 | tVal = def 98 | } 99 | 100 | return NewMUX(cc, index[bit:bit+1], tVal, fVal, out) 101 | } 102 | -------------------------------------------------------------------------------- /compiler/circuits/circ_mux.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package circuits 8 | 9 | import ( 10 | "fmt" 11 | 12 | "github.com/markkurossi/mpc/circuit" 13 | ) 14 | 15 | // NewMUX creates a multiplexer circuit that selects the input t or f 16 | // to output, based on the value of the condition cond. 17 | func NewMUX(cc *Compiler, cond, t, f, out []*Wire) error { 18 | t, f = cc.ZeroPad(t, f) 19 | if len(cond) != 1 || len(t) != len(f) || len(t) != len(out) { 20 | return fmt.Errorf("invalid mux arguments: cond=%d, l=%d, r=%d, out=%d", 21 | len(cond), len(t), len(f), len(out)) 22 | } 23 | 24 | for i := 0; i < len(t); i++ { 25 | w1 := cc.Calloc.Wire() 26 | w2 := cc.Calloc.Wire() 27 | 28 | // w1 = XOR(f[i], t[i]) 29 | cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, f[i], t[i], w1)) 30 | 31 | // w2 = AND(w1, cond) 32 | cc.AddGate(cc.Calloc.BinaryGate(circuit.AND, w1, cond[0], w2)) 33 | 34 | // out[i] = XOR(w2, f[i]) 35 | cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, w2, f[i], out[i])) 36 | } 37 | 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /compiler/circuits/circ_subtractor.go: -------------------------------------------------------------------------------- 1 | // 2 | // circ_subtractor.go 3 | // 4 | // Copyright (c) 2019-2023 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package circuits 10 | 11 | import ( 12 | "github.com/markkurossi/mpc/circuit" 13 | ) 14 | 15 | // NewFullSubtractor creates a full subtractor circuit. 16 | func NewFullSubtractor(cc *Compiler, x, y, cin, d, cout *Wire) { 17 | w1 := cc.Calloc.Wire() 18 | cc.AddGate(cc.Calloc.BinaryGate(circuit.XNOR, y, cin, w1)) 19 | cc.AddGate(cc.Calloc.BinaryGate(circuit.XNOR, x, w1, d)) 20 | 21 | if cout != nil { 22 | w2 := cc.Calloc.Wire() 23 | cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, x, cin, w2)) 24 | 25 | w3 := cc.Calloc.Wire() 26 | cc.AddGate(cc.Calloc.BinaryGate(circuit.AND, w1, w2, w3)) 27 | 28 | cc.AddGate(cc.Calloc.BinaryGate(circuit.XOR, w3, cin, cout)) 29 | } 30 | } 31 | 32 | // NewSubtractor creates a new subtractor circuit implementing z=x-y. 33 | func NewSubtractor(cc *Compiler, x, y, z []*Wire) error { 34 | x, y = cc.ZeroPad(x, y) 35 | if len(x) > len(z) { 36 | x = x[0:len(z)] 37 | y = y[0:len(z)] 38 | } 39 | cin := cc.ZeroWire() 40 | 41 | for i := 0; i < len(x); i++ { 42 | var cout *Wire 43 | if i+1 >= len(x) { 44 | if i+1 >= len(z) { 45 | // N-N=N, overflow, drop carry bit. 46 | cout = nil 47 | } else { 48 | cout = z[i+1] 49 | } 50 | } else { 51 | cout = cc.Calloc.Wire() 52 | } 53 | 54 | // Note y-x here. 55 | NewFullSubtractor(cc, y[i], x[i], cin, z[i], cout) 56 | 57 | cin = cout 58 | } 59 | for i := len(x) + 1; i < len(z); i++ { 60 | z[i] = cc.ZeroWire() 61 | } 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /compiler/circuits/wire_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package circuits 8 | 9 | import ( 10 | "testing" 11 | ) 12 | 13 | func TestWire(t *testing.T) { 14 | w := calloc.Wire() 15 | if w.ID() != UnassignedID { 16 | t.Error("w.ID") 17 | } 18 | w.SetID(42) 19 | if w.ID() != 42 { 20 | t.Error("w.SetID") 21 | } 22 | if !w.Assigned() { 23 | t.Error("Assigned") 24 | } 25 | if w.Output() { 26 | t.Error("Output") 27 | } 28 | w.SetOutput(true) 29 | if !w.Output() { 30 | t.Error("SetOutput") 31 | } 32 | if w.Value() != Unknown { 33 | t.Error("Value") 34 | } 35 | w.SetValue(One) 36 | if w.Value() != One { 37 | t.Error("SetValue") 38 | } 39 | if w.NumOutputs() != 0 { 40 | t.Error("NumOutputs") 41 | } 42 | w.SetNumOutputs(42) 43 | if w.NumOutputs() != 42 { 44 | t.Error("SetNumOutputs") 45 | } 46 | w.SetNumOutputs(1) 47 | if w.NumOutputs() != 1 { 48 | t.Error("SetNumOutputs") 49 | } 50 | if w.Input() != nil { 51 | t.Error("Input") 52 | } 53 | gate := &Gate{} 54 | w.SetInput(gate) 55 | if w.Input() != gate { 56 | t.Error("SetInput") 57 | } 58 | 59 | w.Reset(UnassignedID) 60 | if w.Output() { 61 | t.Error("Reset: Output") 62 | } 63 | if w.Value() != Unknown { 64 | t.Error("Reset: Value") 65 | } 66 | if w.ID() != UnassignedID { 67 | t.Error("Reset: ID") 68 | } 69 | if !w.IsInput() { 70 | t.Error("Reset: IsInput") 71 | } 72 | if w.NumOutputs() != 0 { 73 | t.Error("Reset: NumOutputs") 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /compiler/lexer_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package compiler 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | "io" 13 | "testing" 14 | ) 15 | 16 | var input = `// This is a very basic add circuit. 17 | // But we start this example with 2 comment lines. 18 | 19 | package main 20 | 21 | func main(a, b int4) int5 { 22 | return a + b + 1 23 | } 24 | ` 25 | 26 | func TestLexer(t *testing.T) { 27 | lexer := NewLexer("{data}", bytes.NewReader([]byte(input))) 28 | for { 29 | token, err := lexer.Get() 30 | if err != nil { 31 | if err == io.EOF { 32 | break 33 | } 34 | t.Fatalf("Get failed: %v", err) 35 | } 36 | if false { 37 | fmt.Printf("Token: %s\n", token) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /compiler/mpa/doc.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | // Package mpa implements multi-precision arithmetics. These functions 8 | // are used in constant folding and their implementation uses the same 9 | // digital circuits which are used in garbled circuit evaluation. This 10 | // ensures that they provide identical results for different 11 | // arithmetic calculations. 12 | package mpa 13 | -------------------------------------------------------------------------------- /compiler/mpa/mpint128_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package mpa 8 | 9 | import ( 10 | "testing" 11 | ) 12 | 13 | type int128Test struct { 14 | a int64 15 | b int64 16 | r string 17 | } 18 | 19 | var lsh128Tests = []int128Test{ 20 | { 21 | a: 1, 22 | b: 64, 23 | r: "10000000000000000", 24 | }, 25 | { 26 | a: 1, 27 | b: 128, 28 | r: "0", 29 | }, 30 | } 31 | 32 | func TestInt128Lsh(t *testing.T) { 33 | for _, test := range lsh128Tests { 34 | a := NewInt(test.a, 128) 35 | r := New(128).Lsh(a, uint(test.b)) 36 | result := r.Text(16) 37 | if result != test.r { 38 | t.Errorf("TestInt128Lsh: got %v, expected %v", result, test.r) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /compiler/parser_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2019 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package compiler 8 | 9 | import ( 10 | "bytes" 11 | "fmt" 12 | "os" 13 | "testing" 14 | 15 | "github.com/markkurossi/mpc/compiler/utils" 16 | ) 17 | 18 | const ( 19 | verbose = false 20 | ) 21 | 22 | var parserTests = []string{ 23 | ` 24 | package main 25 | `, 26 | ` 27 | package main 28 | func main() {} 29 | `, 30 | ` 31 | package main 32 | func main(a int4) {} 33 | `, 34 | ` 35 | package main 36 | func main(a int4, b int4) {} 37 | `, 38 | ` 39 | package main 40 | func main(a, b int4) {} 41 | `, 42 | ` 43 | package main 44 | func main(a, b int4) int5 {} 45 | `, 46 | ` 47 | package main 48 | func main(a, b int4) (int5) {} 49 | `, 50 | ` 51 | package main 52 | func main(a, b int4) (int5, int6) {} 53 | `, 54 | ` 55 | package main 56 | func main(a, b int4) (int5) { 57 | return 58 | }`, 59 | ` 60 | package main 61 | func main(a, b int4) (int5) { 62 | return a * b + c * d 63 | }`, 64 | ` 65 | package main 66 | func main(a, b int4) (int5) { 67 | return a * b * c 68 | }`, 69 | ` 70 | package main 71 | func main(a, b int4) (int5) { 72 | return a + b + c * d + e 73 | }`, 74 | ` 75 | package main 76 | func main(a, b int4) (int4) { 77 | if a > b { 78 | return a 79 | } 80 | return b 81 | }`, 82 | ` 83 | package main 84 | func main(a, b int4) (int4) { 85 | if a > b { 86 | return a 87 | } else { 88 | return b 89 | } 90 | }`, 91 | ` 92 | package main 93 | func main(a, b int4) (int4) { 94 | if a > b || a == b { 95 | return a 96 | } else { 97 | return b 98 | } 99 | }`, 100 | ` 101 | package main 102 | func main(a, b int4) int4 { 103 | return max(a, b) 104 | } 105 | 106 | func max(a, b int4) int4 { 107 | if a > b { 108 | return a 109 | } 110 | return v 111 | } 112 | `, 113 | } 114 | 115 | func TestParser(t *testing.T) { 116 | min := 0 117 | for idx, test := range parserTests { 118 | if idx < min { 119 | continue 120 | } 121 | logger := utils.NewLogger(os.Stdout) 122 | parser := NewParser(fmt.Sprintf("{test %d}", idx), nil, logger, 123 | bytes.NewReader([]byte(test))) 124 | _, err := parser.Parse(nil) 125 | if err != nil { 126 | t.Fatalf("Parse test %d failed: %v", idx, err) 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /compiler/ssa/opcodes_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2024 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package ssa 8 | 9 | import ( 10 | "bufio" 11 | "io" 12 | "os" 13 | "regexp" 14 | "strconv" 15 | "testing" 16 | ) 17 | 18 | var reOpcode = regexp.MustCompilePOSIX(`opcode ([[:^space:]]+) \(([^\)]+)\)`) 19 | 20 | func TestOpcodes(t *testing.T) { 21 | operandsByName := make(map[string]Operand) 22 | 23 | for k, v := range operands { 24 | if len(v) > maxOperandLength { 25 | maxOperandLength = len(v) 26 | } 27 | operandsByName[v] = k 28 | } 29 | 30 | filename := "../../docs/apidoc/language,content.md" 31 | file, err := os.Open(filename) 32 | if err != nil { 33 | t.Fatalf("could not open '%v': %v", filename, err) 34 | } 35 | defer file.Close() 36 | 37 | r := bufio.NewReader(file) 38 | var linenum int 39 | var nextCode int64 40 | for { 41 | linenum++ 42 | line, err := r.ReadString('\n') 43 | if len(line) == 0 { 44 | if err != nil { 45 | if err != io.EOF { 46 | t.Fatal(err) 47 | } 48 | break 49 | } 50 | continue 51 | } 52 | m := reOpcode.FindStringSubmatch(line) 53 | if m == nil { 54 | continue 55 | } 56 | code, err := strconv.ParseInt(m[2], 0, 32) 57 | if err != nil { 58 | t.Errorf("%s:%v: invalid opcode '%s': %s", filename, linenum, 59 | m[2], err) 60 | } 61 | if code != nextCode { 62 | t.Errorf("%s:%v: expected opcode 0x%02x, got 0x%02x", 63 | filename, linenum, nextCode, code) 64 | } 65 | nextCode++ 66 | 67 | op, ok := operandsByName[m[1]] 68 | if !ok { 69 | t.Errorf("%s:%v: unknown operand: %v", filename, linenum, m[1]) 70 | } 71 | if int64(op) != code { 72 | t.Errorf("%s:%v: wrong opcode for '%s': got %v, expected %v", 73 | filename, linenum, m[1], code, int64(op)) 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /compiler/ssa/set.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2021 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package ssa 8 | 9 | import ( 10 | "sort" 11 | ) 12 | 13 | // Set implements value set 14 | type Set map[ValueID]Value 15 | 16 | // NewSet creates a new string value set. 17 | func NewSet() Set { 18 | return make(map[ValueID]Value) 19 | } 20 | 21 | // Add adds a value to the set. 22 | func (set Set) Add(val Value) { 23 | set[val.ID] = val 24 | } 25 | 26 | // Remove removes a value from set set. The operation does nothing if 27 | // the value did not exist in the set. 28 | func (set Set) Remove(val Value) { 29 | delete(set, val.ID) 30 | } 31 | 32 | // Copy creates a copy of the set. 33 | func (set Set) Copy() Set { 34 | result := make(map[ValueID]Value) 35 | for k, v := range set { 36 | result[k] = v 37 | } 38 | return result 39 | } 40 | 41 | // Subtract removes the values of the argument set from the set. 42 | func (set Set) Subtract(o Set) { 43 | for _, v := range o { 44 | set.Remove(v) 45 | } 46 | } 47 | 48 | // Array returns the values of the set as an array. 49 | func (set Set) Array() []Value { 50 | var result []Value 51 | for _, v := range set { 52 | result = append(result, v) 53 | } 54 | sort.Slice(result, func(i, j int) bool { 55 | return result[i].ID < result[j].ID 56 | }) 57 | return result 58 | } 59 | -------------------------------------------------------------------------------- /compiler/utils/logger.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package utils 8 | 9 | import ( 10 | "errors" 11 | "fmt" 12 | "io" 13 | "strings" 14 | ) 15 | 16 | // Logger implements compiler logging facility. 17 | type Logger struct { 18 | out io.Writer 19 | } 20 | 21 | // NewLogger creates a new logger outputting to the argument io.Writer. 22 | func NewLogger(out io.Writer) *Logger { 23 | return &Logger{ 24 | out: out, 25 | } 26 | } 27 | 28 | // Errorf logs an error message. 29 | func (l *Logger) Errorf(loc Point, format string, a ...interface{}) error { 30 | msg := fmt.Sprintf(format, a...) 31 | if len(msg) > 0 && msg[len(msg)-1] != '\n' { 32 | msg += "\n" 33 | } 34 | if loc.Undefined() { 35 | fmt.Fprintf(l.out, "%s: %s", loc.Source, msg) 36 | } else { 37 | fmt.Fprintf(l.out, "%s: %s", loc, msg) 38 | } 39 | 40 | idx := strings.IndexRune(msg, '\n') 41 | if idx > 0 { 42 | msg = msg[:idx] 43 | } 44 | return errors.New(msg) 45 | } 46 | 47 | // Warningf logs a warning message. 48 | func (l *Logger) Warningf(loc Point, format string, a ...interface{}) { 49 | msg := fmt.Sprintf(format, a...) 50 | if len(msg) > 0 && msg[len(msg)-1] != '\n' { 51 | msg += "\n" 52 | } 53 | if loc.Undefined() { 54 | fmt.Fprintf(l.out, "%s: warning: %s", loc.Source, msg) 55 | } else { 56 | fmt.Fprintf(l.out, "%s: warning: %s", loc, msg) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /compiler/utils/params.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package utils 8 | 9 | import ( 10 | "io" 11 | ) 12 | 13 | // Params specify compiler parameters. 14 | type Params struct { 15 | Verbose bool 16 | Diagnostics bool 17 | SSAOut io.WriteCloser 18 | SSADotOut io.WriteCloser 19 | MPCLCErrorLoc bool 20 | 21 | // PkgPath defines additional directories to search for imported 22 | // packages. 23 | PkgPath []string 24 | 25 | // MaxVarBits specifies the maximum variable width in bits. 26 | MaxVarBits int 27 | 28 | // MaxLoopUnroll specifies the upper limit for loop unrolling. 29 | MaxLoopUnroll int 30 | 31 | NoCircCompile bool 32 | CircOut io.WriteCloser 33 | CircDotOut io.WriteCloser 34 | CircSvgOut io.WriteCloser 35 | CircFormat string 36 | 37 | CircMultArrayTreshold int 38 | 39 | OptPruneGates bool 40 | 41 | BenchmarkCompile bool 42 | } 43 | 44 | // NewParams returns new compiler params object, initialized with the 45 | // default values. 46 | func NewParams() *Params { 47 | return &Params{ 48 | MaxVarBits: 0x20000, 49 | MaxLoopUnroll: 0x20000, 50 | } 51 | } 52 | 53 | // Close closes all open resources. 54 | func (p *Params) Close() { 55 | if p.SSAOut != nil { 56 | p.SSAOut.Close() 57 | p.SSAOut = nil 58 | } 59 | if p.SSADotOut != nil { 60 | p.SSADotOut.Close() 61 | p.SSADotOut = nil 62 | } 63 | if p.CircOut != nil { 64 | p.CircOut.Close() 65 | p.CircOut = nil 66 | } 67 | if p.CircDotOut != nil { 68 | p.CircDotOut.Close() 69 | p.CircDotOut = nil 70 | } 71 | if p.CircSvgOut != nil { 72 | p.CircSvgOut.Close() 73 | p.CircSvgOut = nil 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /compiler/utils/point.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2023 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package utils 8 | 9 | import ( 10 | "fmt" 11 | "path/filepath" 12 | ) 13 | 14 | // Locator is an interface that implements Location method for 15 | // returning item's input data position. 16 | type Locator interface { 17 | Location() Point 18 | } 19 | 20 | // Point specifies a position in the compiler input data. 21 | type Point struct { 22 | Source string 23 | Line int // 1-based 24 | Col int // 0-based 25 | } 26 | 27 | // Location implements the Locator interface. 28 | func (p Point) Location() Point { 29 | return p 30 | } 31 | 32 | func (p Point) String() string { 33 | return fmt.Sprintf("%s:%d:%d", p.Source, p.Line, p.Col) 34 | } 35 | 36 | // ShortString returns the location string without the file directory 37 | // part. 38 | func (p Point) ShortString() string { 39 | return fmt.Sprintf("%s:%d:%d", filepath.Base(p.Source), p.Line, p.Col) 40 | } 41 | 42 | // Undefined tests if the input position is undefined. 43 | func (p Point) Undefined() bool { 44 | return p.Line == 0 45 | } 46 | -------------------------------------------------------------------------------- /compiler/utils/point_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2021 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package utils 8 | 9 | import ( 10 | "testing" 11 | ) 12 | 13 | func TestPoint(t *testing.T) { 14 | p := Point{} 15 | if !p.Undefined() { 16 | t.Errorf("undefined point is not undefined") 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docs/apidoc/index.toml: -------------------------------------------------------------------------------- 1 | [article] 2 | Title = "Multi-Party Computation Language (MPCL)" 3 | 4 | [meta] 5 | Description = "Documentation of the Multi-Party Computation Language (MPCL)." -------------------------------------------------------------------------------- /docs/apidoc/language.toml: -------------------------------------------------------------------------------- 1 | [article] 2 | Title = "MPCL Language" 3 | 4 | [meta] 5 | Description = "MPCL language documentation." 6 | -------------------------------------------------------------------------------- /docs/ifelse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markkurossi/mpc/8a4e258ee2b98a9e96e8f18193a6813dc3281d36/docs/ifelse.png -------------------------------------------------------------------------------- /docs/max.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markkurossi/mpc/8a4e258ee2b98a9e96e8f18193a6813dc3281d36/docs/max.png -------------------------------------------------------------------------------- /docs/mpcc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markkurossi/mpc/8a4e258ee2b98a9e96e8f18193a6813dc3281d36/docs/mpcc.png -------------------------------------------------------------------------------- /docs/mpcl-compiler.ditaa: -------------------------------------------------------------------------------- 1 | 2 | 3 | +-----+ +-----+ +------+ /-----\ +--------+ +----------+ /-----\ 4 | | | | | | | | | | | | | | | 5 | |.mpcl|->|Lexer+->|Parser+->| AST +->|Constant+->| SSA +->| SSA +-\ 6 | | {d}| | | | | | | |Folding | |Generation| | | | 7 | +-----+ +-----+ +------+ \-----/ +--------+ +----------+ \-----/ | 8 | | 9 | /-------------------------------------------------------------------------/ 10 | | 11 | | +------+ 12 | | | | 13 | | /->|.mpclc| 14 | | +------------+ +----------+ +-------+ +-------------+ | | {d}| 15 | | | | | | | Dead | | | | +------+ 16 | \->| Peephole +->| Circuit +->| Gate +->| Circuit +-+ 17 | |Optimization| |Generation| |Pruning| |Serialization| | +---------+ 18 | +------------+ +----------+ +-------+ +-------------+ | | Garbler | 19 | \->| / | 20 | |Evaluator| 21 | +---------+ 22 | -------------------------------------------------------------------------------- /docs/mpcl-compiler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markkurossi/mpc/8a4e258ee2b98a9e96e8f18193a6813dc3281d36/docs/mpcl-compiler.png -------------------------------------------------------------------------------- /docs/mpcl.iso-ebnf: -------------------------------------------------------------------------------- 1 | SourceFile = PackageClause, { ImportDecl }, { TopLevelDecl }; 2 | 3 | PackageClause = 'package', identifier; 4 | 5 | ImportDecl = 'import', '(', { ImportSpec }, ')'; 6 | ImportSpec = { identifier }, string_lit; 7 | 8 | TopLevelDecl = Declaration | FunctionDecl | MethodDecl; 9 | 10 | Declaration = ConstDecl | TypeDecl | VarDecl; 11 | 12 | 13 | ConstDecl = 'const', ( ConstSpec | '(', { ConstSpec }, ')' ); 14 | ConstSpec = IdentifierList, [ Type ], '=', ExpressionList; 15 | 16 | TypeDecl = 'type', ( TypeSpec | '(', { TypeSpec }, ')' ); 17 | TypeSpec = AliasDecl | TypeDef; 18 | AliasDecl = identifier, '=', Type; 19 | TypeDef = identifier, Type; 20 | 21 | Type = TypeName | TypeLit | '(', Type, ')'; 22 | TypeName = identifier | QualifiedIdent; 23 | TypeLit = ArrayType | StructType | SliceType; 24 | 25 | ArrayType = '[', Expression, ']', Type; 26 | 27 | StructType = 'struct', '{', { FieldDecl }, '}'; 28 | FieldDecl = IdentifierList, Type; 29 | 30 | SliceType = '[]', Type; 31 | 32 | 33 | QualifiedIdent = identifier, '.', identifier; 34 | 35 | 36 | VarDecl = 'var', ( VarSpec | '(', { VarSpec }, ')' ); 37 | VarSpec = IdentifierList, ( Type, [ '=', ExpressionList ] 38 | | '=', ExpressionList ); 39 | 40 | 41 | ExpressionList = Expression, { ',', Expression }; 42 | 43 | 44 | FunctionDecl = 'func', identifier, Signature, Block; 45 | 46 | Signature = Parameters, { Result }; 47 | 48 | Result = Parameters | Type; 49 | 50 | Parameters = '(', { ParameterList }, ')'; 51 | 52 | ParameterList = ParameterDecl, { ',', ParameterDecl }; 53 | 54 | ParameterDecl = { IdentifierList }, Type; 55 | 56 | IdentifierList = identifier, { ',', identifier }; 57 | 58 | Block = '{', { Statement }, '}'; 59 | 60 | Statement = Declaration | IfStmt | ReturnStmt | ForStmt | SimpleStmt; 61 | 62 | IfStmt = 'if', Expression, Block, [ 'else', ( IfStmt | Block ) ]; 63 | ReturnStmt = 'return', [ ExpressionList ]; 64 | ForStmt = 'for', ForClause, Block; 65 | ForClause = SimpleStmt, ';', Expression, ';', SimpleStmt; 66 | 67 | SimpleStmt = Expression | IncDecStmt | Assignment | ShortVarDecl; 68 | 69 | IncDecStmt = Expression, ( '++' | '--' ); 70 | 71 | Assignment = ExpressionList, assign_op, ExpressionList; 72 | 73 | assign_op = [ add_op | mul_op ], '='; 74 | 75 | add_op = '+' | '-' | '|' | '^'; 76 | mul_op = '*' | '/' | '%' | '<<' | '>>' | '&' | '&^'; 77 | 78 | ShortVarDecl = IdentifierList, ':=', ExpressionList; 79 | -------------------------------------------------------------------------------- /docs/ref/ecdh/GNUmakefile: -------------------------------------------------------------------------------- 1 | 2 | MPCL := ../../../pkg/crypto/curve25519 3 | 4 | all: diff 5 | 6 | diff: 7 | diff -u curve25519/doc.go $(MPCL)/doc.mpcl || true 8 | diff -u curve25519/curve25519.go $(MPCL)/curve25519.mpcl || true 9 | -------------------------------------------------------------------------------- /docs/ref/ecdh/curve25519/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 The Go Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package curve25519 provides an implementation of scalar multiplication on 6 | // the elliptic curve known as curve25519. See https://cr.yp.to/ecdh.html 7 | package curve25519 8 | 9 | // basePoint is the x coordinate of the generator of the curve. 10 | var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 11 | 12 | // ScalarMult sets dst to the product in*base where dst and base are the x 13 | // coordinates of group points and all values are in little-endian form. 14 | func ScalarMult(dst, in, base *[32]byte) { 15 | scalarMult(dst, in, base) 16 | } 17 | 18 | // ScalarBaseMult sets dst to the product in*base where dst and base are the x 19 | // coordinates of group points, base is the standard generator and all values 20 | // are in little-endian form. 21 | func ScalarBaseMult(dst, in *[32]byte) { 22 | ScalarMult(dst, in, &basePoint) 23 | } 24 | -------------------------------------------------------------------------------- /docs/ref/ecdh/main.go: -------------------------------------------------------------------------------- 1 | // ecdh in Go 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/rand" 7 | "fmt" 8 | "log" 9 | 10 | "github.com/markkurossi/mpc/docs/ref/ecdh/curve25519" 11 | ) 12 | 13 | func main() { 14 | 15 | var privateKey [32]byte 16 | 17 | _, err := rand.Read(privateKey[:]) 18 | if err != nil { 19 | log.Fatal(err) 20 | } 21 | for i := 0; i < len(privateKey); i++ { 22 | privateKey[i] = byte((i % 8) + 1) 23 | } 24 | 25 | var publicKey [32]byte 26 | curve25519.ScalarBaseMult(&publicKey, &privateKey) 27 | 28 | fmt.Printf("Alice priv : %x\n", privateKey) 29 | fmt.Printf("Alice pub : %x\n", publicKey) 30 | 31 | var privateKey2 [32]byte 32 | _, err = rand.Read(privateKey2[:]) 33 | if err != nil { 34 | log.Fatal(err) 35 | } 36 | for i := 0; i < len(privateKey2); i++ { 37 | privateKey2[i] = byte((i % 16) + 1) 38 | } 39 | 40 | var publicKey2 [32]byte 41 | curve25519.ScalarBaseMult(&publicKey2, &privateKey2) 42 | 43 | var out1, out2 [32]byte 44 | 45 | fmt.Printf("Bob priv : %x\n", privateKey2) 46 | fmt.Printf("Bob pub : %x\n", publicKey2) 47 | 48 | fmt.Printf(" ScalarMult(0x%x,\n\t 0x%x)\n", privateKey, publicKey2) 49 | curve25519.ScalarMult(&out1, &privateKey, &publicKey2) 50 | curve25519.ScalarMult(&out2, &privateKey2, &publicKey) 51 | 52 | fmt.Printf("Alice shared: %x\n", out1) 53 | fmt.Printf("Bob shared : %x\n", out2) 54 | 55 | } 56 | -------------------------------------------------------------------------------- /docs/ref/ed25519/GNUmakefile: -------------------------------------------------------------------------------- 1 | MPCL := ../../../pkg/crypto/ed25519/internal/edwards25519 2 | 3 | all: diff 4 | 5 | diff: 6 | diff -u edwards25519/const.go $(MPCL)/const.mpcl || true 7 | diff -u edwards25519/edwards25519.go $(MPCL)/ed25519.mpcl || true 8 | -------------------------------------------------------------------------------- /docs/ref/sha1/GNUmakefile: -------------------------------------------------------------------------------- 1 | MPCL := ../../../pkg/crypto/sha1 2 | 3 | all: diff 4 | 5 | diff: 6 | diff -u crypto/sha1/block.go $(MPCL)/block.mpcl || true 7 | diff -u crypto/sha1/sha1.go $(MPCL)/sha1.mpcl || true 8 | -------------------------------------------------------------------------------- /docs/ref/sha1/crypto/sha1/block.go: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright 2009 The Go Authors. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | package sha1 8 | 9 | import ( 10 | "math/bits" 11 | ) 12 | 13 | const ( 14 | _K0 = 0x5A827999 15 | _K1 = 0x6ED9EBA1 16 | _K2 = 0x8F1BBCDC 17 | _K3 = 0xCA62C1D6 18 | ) 19 | 20 | // Block is a portable, pure Go version of the SHA-1 block step. It's 21 | // used by sha1block_generic.go and tests. 22 | func Block(p []byte, state [5]uint32) [5]uint32 { 23 | var w [16]uint32 24 | 25 | if len(p) != chunk { 26 | panic("invalid input size") 27 | } 28 | 29 | h0, h1, h2, h3, h4 := state[0], state[1], state[2], state[3], state[4] 30 | 31 | // Can interlace the computation of w with the 32 | // rounds below if needed for speed. 33 | for i := 0; i < 16; i++ { 34 | j := i * 4 35 | w[i] = uint32(p[j])<<24 | uint32(p[j+1])<<16 | uint32(p[j+2])<<8 | uint32(p[j+3]) 36 | } 37 | 38 | a, b, c, d, e := h0, h1, h2, h3, h4 39 | 40 | // Each of the four 20-iteration rounds 41 | // differs only in the computation of f and 42 | // the choice of K (_K0, _K1, etc). 43 | i := 0 44 | for ; i < 16; i++ { 45 | f := b&c | (^b)&d 46 | t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + _K0 47 | a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d 48 | } 49 | for ; i < 20; i++ { 50 | tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf] 51 | w[i&0xf] = bits.RotateLeft32(tmp, 1) 52 | 53 | f := b&c | (^b)&d 54 | t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + _K0 55 | a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d 56 | } 57 | for ; i < 40; i++ { 58 | tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf] 59 | w[i&0xf] = bits.RotateLeft32(tmp, 1) 60 | f := b ^ c ^ d 61 | t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + _K1 62 | a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d 63 | } 64 | for ; i < 60; i++ { 65 | tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf] 66 | w[i&0xf] = bits.RotateLeft32(tmp, 1) 67 | f := ((b | c) & d) | (b & c) 68 | t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + _K2 69 | a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d 70 | } 71 | for ; i < 80; i++ { 72 | tmp := w[(i-3)&0xf] ^ w[(i-8)&0xf] ^ w[(i-14)&0xf] ^ w[(i)&0xf] 73 | w[i&0xf] = bits.RotateLeft32(tmp, 1) 74 | f := b ^ c ^ d 75 | t := bits.RotateLeft32(a, 5) + f + e + w[i&0xf] + _K3 76 | a, b, c, d, e = t, a, bits.RotateLeft32(b, 30), c, d 77 | } 78 | 79 | h0 += a 80 | h1 += b 81 | h2 += c 82 | h3 += d 83 | h4 += e 84 | 85 | state[0] = h0 86 | state[1] = h1 87 | state[2] = h2 88 | state[3] = h3 89 | state[4] = h4 90 | 91 | return state 92 | } 93 | -------------------------------------------------------------------------------- /docs/ref/sha1/crypto/sha1/sha1.go: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright 2009 The Go Authors. All rights reserved. 4 | // Use of this source code is governed by a BSD-style 5 | // license that can be found in the LICENSE file. 6 | 7 | // Package sha1 implements the SHA-1 hash algorithm as defined in RFC 3174. 8 | // 9 | // SHA-1 is cryptographically broken and should not be used for secure 10 | // applications. 11 | package sha1 12 | 13 | import ( 14 | "encoding/binary" 15 | ) 16 | 17 | // The size of a SHA-1 checksum in bytes. 18 | const Size = 20 19 | 20 | // The blocksize of SHA-1 in bytes. 21 | const BlockSize = 64 22 | 23 | const ( 24 | chunk = 64 25 | init0 = 0x67452301 26 | init1 = 0xEFCDAB89 27 | init2 = 0x98BADCFE 28 | init3 = 0x10325476 29 | init4 = 0xC3D2E1F0 30 | ) 31 | 32 | // Sum returns the SHA-1 checksum of the data. 33 | func Sum(data []byte) [Size]byte { 34 | state := [5]uint32{init0, init1, init2, init3, init4} 35 | length := uint64(len(data)) 36 | 37 | for len(data) >= chunk { 38 | state = Block(data[:chunk], state) 39 | data = data[chunk:] 40 | } 41 | 42 | // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. 43 | 44 | var tmp [128 + 8]byte // padding + length buffer 45 | copy(tmp[:], data) 46 | i := len(data) 47 | tmp[i] = 0x80 48 | 49 | var t uint64 50 | if length%64 < 56 { 51 | t = 56 - length%64 52 | } else { 53 | t = 64 + 56 - length%64 54 | } 55 | t += uint64(i) 56 | 57 | // Length in bits. 58 | length <<= 3 59 | padlen := tmp[:t+8] 60 | binary.BigEndian.PutUint64(padlen[t:], length) 61 | 62 | for len(padlen) >= chunk { 63 | state = Block(padlen[:chunk], state) 64 | padlen = padlen[chunk:] 65 | } 66 | 67 | var digest [Size]byte 68 | 69 | binary.BigEndian.PutUint32(digest[0:], state[0]) 70 | binary.BigEndian.PutUint32(digest[4:], state[1]) 71 | binary.BigEndian.PutUint32(digest[8:], state[2]) 72 | binary.BigEndian.PutUint32(digest[12:], state[3]) 73 | binary.BigEndian.PutUint32(digest[16:], state[4]) 74 | 75 | return digest 76 | } 77 | -------------------------------------------------------------------------------- /docs/ref/sha1/main.go: -------------------------------------------------------------------------------- 1 | // SHA-1 in Go 2 | 3 | package main 4 | 5 | import ( 6 | "fmt" 7 | 8 | "github.com/markkurossi/mpc/docs/ref/sha1/crypto/sha1" 9 | ) 10 | 11 | func main() { 12 | data := []byte("This page intentionally left blank.") 13 | fmt.Printf("%x =>\n", data) 14 | fmt.Printf("%x\n", sha1.SumGo(data)) 15 | fmt.Printf("%x\n", sha1.Sum(data)) 16 | fmt.Println() 17 | 18 | data = []byte("----------------------------------------------------------------+!!") 19 | fmt.Printf("%x =>\n", data) 20 | fmt.Printf("%x\n", sha1.SumGo(data)) 21 | fmt.Printf("%x\n", sha1.Sum(data)) 22 | fmt.Println() 23 | 24 | data = []byte("----------------------------------------------------------------+---------------------------------------------------------------+") 25 | fmt.Printf("%x\n", sha1.SumGo(data)) 26 | fmt.Printf("%x\n", sha1.Sum(data)) 27 | fmt.Println() 28 | 29 | data = []byte("") 30 | fmt.Printf("%x\n", sha1.SumGo(data)) 31 | fmt.Printf("%x\n", sha1.Sum(data)) 32 | } 33 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/markkurossi/mpc 2 | 3 | go 1.21 4 | 5 | require ( 6 | github.com/markkurossi/crypto v0.0.0-20230320090745-b923f1c5109e 7 | github.com/markkurossi/tabulate v0.0.0-20230223130100-d4965869b123 8 | github.com/markkurossi/text v0.0.0-20240111094439-6ab4a36f087d 9 | ) 10 | 11 | require golang.org/x/text v0.14.0 // indirect 12 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/markkurossi/crypto v0.0.0-20230320090745-b923f1c5109e h1:zz+aZRtU/rGv4krMq4/UglM/5Wkrv8NcJXJU5MANtUI= 2 | github.com/markkurossi/crypto v0.0.0-20230320090745-b923f1c5109e/go.mod h1:+mhV8wp86RN6GgVyETgAd6oY+D3xLNESWKty2fGNg5U= 3 | github.com/markkurossi/tabulate v0.0.0-20230223130100-d4965869b123 h1:aGg9ACNKrIa6lZ18dNT9ZsFcXga3obyOAl5Tiyx2txE= 4 | github.com/markkurossi/tabulate v0.0.0-20230223130100-d4965869b123/go.mod h1:qPNWLW3h4173ZWYHjOgJ1wbvNyLuE1fboZilv97Aq7k= 5 | github.com/markkurossi/text v0.0.0-20240111094439-6ab4a36f087d h1:x9hJWGgElhestm6lVc12GGg7+p3rIdqS29tIRCNI5WQ= 6 | github.com/markkurossi/text v0.0.0-20240111094439-6ab4a36f087d/go.mod h1:NdoMTINXTG7tKD94hd9UevVM9Jtc4o6giWNaoo+sOQ0= 7 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 8 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 9 | -------------------------------------------------------------------------------- /mult64.iql: -------------------------------------------------------------------------------- 1 | /* -*- sql -*- */ 2 | 3 | SELECT Level, 4 | Width, 5 | HBAR(Width, 0, MAX(Width), 10) AS "[Width]" 6 | FROM ```datauri:text/csv 7 | Level, Width 8 | 0,917 9 | 1,24 10 | 2,24 11 | 3,29 12 | 4,36 13 | 5,37 14 | 6,38 15 | 7,29 16 | 8,43 17 | 9,59 18 | 10,44 19 | 11,40 20 | 12,61 21 | 13,53 22 | 14,58 23 | 15,82 24 | 16,50 25 | 17,54 26 | 18,97 27 | 19,68 28 | 20,69 29 | 21,100 30 | 22,64 31 | 23,78 32 | 24,123 33 | 25,79 34 | 26,75 35 | 27,121 36 | 28,88 37 | 29,93 38 | 30,138 39 | 31,85 40 | 32,89 41 | 33,149 42 | 34,103 43 | 35,104 44 | 36,148 45 | 37,99 46 | 38,113 47 | 39,168 48 | 40,114 49 | 41,109 50 | 42,165 51 | 43,119 52 | 44,123 53 | 45,171 54 | 46,137 55 | 47,103 56 | 48,115 57 | 49,120 58 | 50,112 59 | 51,108 60 | 52,107 61 | 53,116 62 | 54,122 63 | 55,115 64 | 56,110 65 | 57,110 66 | 58,116 67 | 59,123 68 | 60,121 69 | 61,106 70 | 62,110 71 | 63,123 72 | 64,117 73 | 65,120 74 | 66,115 75 | 67,103 76 | 68,123 77 | 69,128 78 | 70,110 79 | 71,117 80 | 72,117 81 | 73,112 82 | 74,126 83 | 75,122 84 | 76,106 85 | 77,115 86 | 78,121 87 | 79,110 88 | 80,117 89 | 81,114 90 | 82,104 91 | 83,115 92 | 84,114 93 | 85,108 94 | 86,109 95 | 87,108 96 | 88,104 97 | 89,109 98 | 90,106 99 | 91,101 100 | 92,99 101 | 93,96 102 | 94,94 103 | 95,91 104 | 96,87 105 | 97,83 106 | 98,80 107 | 99,81 108 | 100,76 109 | 101,74 110 | 102,69 111 | 103,68 112 | 104,66 113 | 105,64 114 | 106,60 115 | 107,56 116 | 108,54 117 | 109,54 118 | 110,49 119 | 111,48 120 | 112,42 121 | 113,41 122 | 114,40 123 | 115,37 124 | 116,33 125 | 117,30 126 | 118,25 127 | 119,26 128 | 120,22 129 | 121,19 130 | 122,19 131 | 123,17 132 | 124,16 133 | 125,13 134 | 126,12 135 | 127,14 136 | 128,14 137 | 129,10 138 | 130,9 139 | 131,9 140 | 132,8 141 | 133,9 142 | 134,8 143 | 135,6 144 | 136,6 145 | 137,7 146 | 138,6 147 | 139,6 148 | 140,7 149 | 141,6 150 | 142,6 151 | 143,7 152 | 144,6 153 | 145,6 154 | 146,7 155 | 147,6 156 | 148,6 157 | 149,7 158 | 150,6 159 | 151,6 160 | 152,7 161 | 153,6 162 | 154,6 163 | 155,7 164 | 156,6 165 | 157,6 166 | 158,7 167 | 159,6 168 | 160,6 169 | 161,7 170 | 162,6 171 | 163,6 172 | 164,7 173 | 165,6 174 | 166,6 175 | 167,7 176 | 168,6 177 | 169,6 178 | 170,7 179 | 171,6 180 | 172,6 181 | 173,7 182 | 174,6 183 | 175,6 184 | 176,7 185 | 177,5 186 | 178,4 187 | 179,5 188 | 180,3 189 | 181,3 190 | 182,4 191 | 183,3 192 | 184,3 193 | 185,4 194 | 186,3 195 | 187,3 196 | 188,4 197 | 189,3 198 | 190,3 199 | 191,4 200 | 192,3 201 | 193,3 202 | 194,4 203 | 195,3 204 | 196,3 205 | 197,4 206 | 198,3 207 | 199,3 208 | 200,4 209 | 201,3 210 | 202,3 211 | 203,4 212 | 204,3 213 | 205,3 214 | 206,4 215 | 207,3 216 | 208,3 217 | 209,4 218 | 210,3 219 | 211,3 220 | 212,4 221 | 213,3 222 | 214,3 223 | 215,3 224 | 216,1 225 | 217,1 226 | 218,1 227 | ```; 228 | -------------------------------------------------------------------------------- /ot/README.md: -------------------------------------------------------------------------------- 1 | # Oblivious Transfer 2 | 3 | This module implements the Oblivous Transfer with the following 4 | algorithms: 5 | 6 | - RSA: simple RSA encryption based OT. Each transfer requires one RSA 7 | operation. 8 | - Chou Orlandi OT: Diffie-Hellman - like fast OT algorithm. 9 | 10 | ## Performance 11 | 12 | | Algorithm | ns/op | ops/s | 13 | | :----------- | ---------: | ------: | 14 | | RSA-512 | 252557 | 3960 | 15 | | RSA-1024 | 1256961 | 796 | 16 | | RSA-2048 | 7785958 | 128 | 17 | | CO-batch-1 | 170791 | 5855 | 18 | | CO-batch-2 | 269399 | 7424 | 19 | | CO-batch-4 | 468161 | 8544 | 20 | | CO-batch-8 | 877664 | 9115 | 21 | | CO-batch-16 | 1706184 | 9378 | 22 | | CO-batch-32 | 3273137 | 9777 | 23 | | CO-batch-64 | 6480310 | 9876 | 24 | | CO-batch-128 | 12845639 | 9964 | 25 | -------------------------------------------------------------------------------- /ot/co_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // rsa_test.go 3 | // 4 | // Copyright (c) 2023 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package ot 10 | 11 | import ( 12 | "bytes" 13 | "crypto/rand" 14 | "testing" 15 | ) 16 | 17 | func TestCO(t *testing.T) { 18 | l0, _ := NewLabel(rand.Reader) 19 | l1, _ := NewLabel(rand.Reader) 20 | 21 | sender := NewCOSender() 22 | receiver := NewCOReceiver(sender.Curve()) 23 | 24 | var l0Buf, l1Buf LabelData 25 | l0Data := l0.Bytes(&l0Buf) 26 | l1Data := l1.Bytes(&l1Buf) 27 | 28 | sXfer, err := sender.NewTransfer(l0Data, l1Data) 29 | if err != nil { 30 | t.Fatalf("COSender.NewTransfer: %v", err) 31 | } 32 | var bit uint = 1 33 | 34 | rXfer, err := receiver.NewTransfer(bit) 35 | if err != nil { 36 | t.Fatalf("COReceiver.NewTransfer: %v", err) 37 | } 38 | rXfer.ReceiveA(sXfer.A()) 39 | sXfer.ReceiveB(rXfer.B()) 40 | result := rXfer.ReceiveE(sXfer.E()) 41 | 42 | var ret int 43 | if bit == 0 { 44 | ret = bytes.Compare(result, l0Data[:]) 45 | } else { 46 | ret = bytes.Compare(result, l1Data[:]) 47 | } 48 | if ret != 0 { 49 | t.Errorf("Verify failed") 50 | } 51 | } 52 | 53 | func BenchmarkCO(b *testing.B) { 54 | l0, _ := NewLabel(rand.Reader) 55 | l1, _ := NewLabel(rand.Reader) 56 | 57 | sender := NewCOSender() 58 | receiver := NewCOReceiver(sender.Curve()) 59 | 60 | b.ResetTimer() 61 | 62 | var l0Buf, l1Buf LabelData 63 | for i := 0; i < b.N; i++ { 64 | l0Data := l0.Bytes(&l0Buf) 65 | l1Data := l1.Bytes(&l1Buf) 66 | sXfer, err := sender.NewTransfer(l0Data, l1Data) 67 | if err != nil { 68 | b.Fatalf("COSender.NewTransfer: %v", err) 69 | } 70 | bit := uint(i % 2) 71 | 72 | rXfer, err := receiver.NewTransfer(bit) 73 | if err != nil { 74 | b.Fatalf("COReceiver.NewTransfer: %v", err) 75 | } 76 | rXfer.ReceiveA(sXfer.A()) 77 | sXfer.ReceiveB(rXfer.B()) 78 | result := rXfer.ReceiveE(sXfer.E()) 79 | 80 | var ret int 81 | if bit == 0 { 82 | ret = bytes.Compare(l0Data[:], result) 83 | } else { 84 | ret = bytes.Compare(l1Data[:], result) 85 | } 86 | if ret != 0 { 87 | b.Fatal("Verify failed") 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /ot/io.go: -------------------------------------------------------------------------------- 1 | // 2 | // io.go 3 | // 4 | // Copyright (c) 2023-2024 Markku Rossi 5 | // 6 | // All rights reserved. 7 | 8 | package ot 9 | 10 | import ( 11 | "math/big" 12 | ) 13 | 14 | // IO defines an I/O interface to communicate between peers. 15 | type IO interface { 16 | // SendByte sends a byte value. 17 | SendByte(val byte) error 18 | 19 | // SendUint32 sends an uint32 value. 20 | SendUint32(val int) error 21 | 22 | // SendData sends binary data. 23 | SendData(val []byte) error 24 | 25 | // Flush flushed any pending data in the connection. 26 | Flush() error 27 | 28 | // ReceiveByte receives a byte value. 29 | ReceiveByte() (byte, error) 30 | 31 | // ReceiveUint32 receives an uint32 value. 32 | ReceiveUint32() (int, error) 33 | 34 | // ReceiveData receives binary data. 35 | ReceiveData() ([]byte, error) 36 | } 37 | 38 | // SendString sends a string value. 39 | func SendString(io IO, str string) error { 40 | return io.SendData([]byte(str)) 41 | } 42 | 43 | // ReceiveString receives a string value. 44 | func ReceiveString(io IO) (string, error) { 45 | data, err := io.ReceiveData() 46 | if err != nil { 47 | return "", err 48 | } 49 | return string(data), nil 50 | } 51 | 52 | // ReceiveBigInt receives a bit.Int from the connection. 53 | func ReceiveBigInt(io IO) (*big.Int, error) { 54 | data, err := io.ReceiveData() 55 | if err != nil { 56 | return nil, err 57 | } 58 | return big.NewInt(0).SetBytes(data), nil 59 | } 60 | -------------------------------------------------------------------------------- /ot/label.go: -------------------------------------------------------------------------------- 1 | // 2 | // label.go 3 | // 4 | // Copyright (c) 2019-2024 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package ot 10 | 11 | import ( 12 | "encoding/binary" 13 | "fmt" 14 | "io" 15 | ) 16 | 17 | // Wire implements a wire with 0 and 1 labels. 18 | type Wire struct { 19 | L0 Label 20 | L1 Label 21 | } 22 | 23 | func (w Wire) String() string { 24 | return fmt.Sprintf("%s/%s", w.L0, w.L1) 25 | } 26 | 27 | // Label implements a 128 bit wire label. 28 | type Label struct { 29 | D0 uint64 30 | D1 uint64 31 | } 32 | 33 | // LabelData contains lable data as byte array. 34 | type LabelData [16]byte 35 | 36 | func (l Label) String() string { 37 | return fmt.Sprintf("%016x%016x", l.D0, l.D1) 38 | } 39 | 40 | // Equal test if the labels are equal. 41 | func (l Label) Equal(o Label) bool { 42 | return l.D0 == o.D0 && l.D1 == o.D1 43 | } 44 | 45 | // NewLabel creates a new random label. 46 | func NewLabel(rand io.Reader) (Label, error) { 47 | var buf LabelData 48 | var label Label 49 | 50 | if _, err := rand.Read(buf[:]); err != nil { 51 | return label, err 52 | } 53 | label.SetData(&buf) 54 | return label, nil 55 | } 56 | 57 | // NewTweak creates a new label from the tweak value. 58 | func NewTweak(tweak uint32) Label { 59 | return Label{ 60 | D1: uint64(tweak), 61 | } 62 | } 63 | 64 | // S tests the label's S bit. 65 | func (l Label) S() bool { 66 | return (l.D0 & 0x8000000000000000) != 0 67 | } 68 | 69 | // SetS sets the label's S bit. 70 | func (l *Label) SetS(set bool) { 71 | if set { 72 | l.D0 |= 0x8000000000000000 73 | } else { 74 | l.D0 &= 0x7fffffffffffffff 75 | } 76 | } 77 | 78 | // Mul2 multiplies the label by 2. 79 | func (l *Label) Mul2() { 80 | l.D0 <<= 1 81 | l.D0 |= (l.D1 >> 63) 82 | l.D1 <<= 1 83 | } 84 | 85 | // Mul4 multiplies the label by 4. 86 | func (l *Label) Mul4() { 87 | l.D0 <<= 2 88 | l.D0 |= (l.D1 >> 62) 89 | l.D1 <<= 2 90 | } 91 | 92 | // Xor xors the label with the argument label. 93 | func (l *Label) Xor(o Label) { 94 | l.D0 ^= o.D0 95 | l.D1 ^= o.D1 96 | } 97 | 98 | // GetData gets the labels as label data. 99 | func (l Label) GetData(buf *LabelData) { 100 | binary.BigEndian.PutUint64(buf[0:8], l.D0) 101 | binary.BigEndian.PutUint64(buf[8:16], l.D1) 102 | } 103 | 104 | // SetData sets the labels from label data. 105 | func (l *Label) SetData(data *LabelData) { 106 | l.D0 = binary.BigEndian.Uint64((*data)[0:8]) 107 | l.D1 = binary.BigEndian.Uint64((*data)[8:16]) 108 | } 109 | 110 | // Bytes returns the label data as bytes. 111 | func (l Label) Bytes(buf *LabelData) []byte { 112 | l.GetData(buf) 113 | return buf[:] 114 | } 115 | 116 | // SetBytes sets the label data from bytes. 117 | func (l *Label) SetBytes(data []byte) { 118 | l.D0 = binary.BigEndian.Uint64(data[0:8]) 119 | l.D1 = binary.BigEndian.Uint64(data[8:16]) 120 | } 121 | -------------------------------------------------------------------------------- /ot/label_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // label_test.go 3 | // 4 | // Copyright (c) 2019-2023 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package ot 10 | 11 | import ( 12 | "testing" 13 | ) 14 | 15 | func BenchmarkLabelMul2(b *testing.B) { 16 | var l Label 17 | 18 | for i := 0; i < b.N; i++ { 19 | l.Mul2() 20 | } 21 | } 22 | 23 | func BenchmarkLabelMul4(b *testing.B) { 24 | var l Label 25 | 26 | for i := 0; i < b.N; i++ { 27 | l.Mul4() 28 | } 29 | } 30 | 31 | func BenchmarkLabelXor(b *testing.B) { 32 | var l0, l1 Label 33 | 34 | for i := 0; i < b.N; i++ { 35 | l0.Xor(l1) 36 | } 37 | } 38 | 39 | func TestLabel(t *testing.T) { 40 | label := &Label{ 41 | D0: 0xffffffffffffffff, 42 | D1: 0xffffffffffffffff, 43 | } 44 | 45 | label.SetS(true) 46 | if label.D0 != 0xffffffffffffffff { 47 | t.Fatal("Failed to set S-bit") 48 | } 49 | 50 | label.SetS(false) 51 | if label.D0 != 0x7fffffffffffffff { 52 | t.Fatalf("Failed to clear S-bit: %x", label.D0) 53 | } 54 | 55 | label = &Label{ 56 | D1: 0xffffffffffffffff, 57 | } 58 | label.Mul2() 59 | if label.D0 != 0x1 { 60 | t.Fatalf("Mul2 D0 failed") 61 | } 62 | if label.D1 != 0xfffffffffffffffe { 63 | t.Fatalf("Mul2 D1 failed: %x", label.D1) 64 | } 65 | 66 | label = &Label{ 67 | D1: 0xffffffffffffffff, 68 | } 69 | label.Mul4() 70 | if label.D0 != 0x3 { 71 | t.Fatalf("Mul4 D0 failed") 72 | } 73 | if label.D1 != 0xfffffffffffffffc { 74 | t.Fatalf("Mul4 D1 failed") 75 | } 76 | 77 | val := uint64(0x5555555555555555) 78 | label = &Label{ 79 | D0: val, 80 | D1: val << 1, 81 | } 82 | label.Xor(Label{ 83 | D0: 0xffffffffffffffff, 84 | D1: 0xffffffffffffffff, 85 | }) 86 | if label.D0 != val<<1 { 87 | t.Errorf("Xor failed: D0=%x, expected=%x", label.D0, val<<1) 88 | } 89 | if label.D1 != val { 90 | t.Errorf("Xor failed: D1=%x, expected=%x", label.D1, val) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /ot/mpint/mpint.go: -------------------------------------------------------------------------------- 1 | // 2 | // mpint.go 3 | // 4 | // Copyright (c) 2019 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package mpint 10 | 11 | import ( 12 | "math/big" 13 | ) 14 | 15 | // FromBytes creates a big.Int from the data. 16 | func FromBytes(data []byte) *big.Int { 17 | return big.NewInt(0).SetBytes(data) 18 | } 19 | 20 | // Add adds two big.Int numbers and returns the result as a new 21 | // big.Int. 22 | func Add(a, b *big.Int) *big.Int { 23 | return big.NewInt(0).Add(a, b) 24 | } 25 | 26 | // Sub subtracts two big.Int numbers and returns the result as a new 27 | // big.Int. 28 | func Sub(a, b *big.Int) *big.Int { 29 | return big.NewInt(0).Sub(a, b) 30 | } 31 | 32 | // Exp computes x^y MOD m and returns the result as a new big.Int. 33 | func Exp(x, y, m *big.Int) *big.Int { 34 | return big.NewInt(0).Exp(x, y, m) 35 | } 36 | 37 | // Mod computes x%y and returns the result as a new big.Int. 38 | func Mod(x, y *big.Int) *big.Int { 39 | return big.NewInt(0).Mod(x, y) 40 | } 41 | -------------------------------------------------------------------------------- /ot/mpint/mpint_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // mpint_test.go 3 | // 4 | // Copyright (c) 2019 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package mpint 10 | 11 | import ( 12 | "testing" 13 | ) 14 | 15 | var ( 16 | oneData = []byte{0x1} 17 | twoData = []byte{0x2} 18 | threeData = []byte{0x3} 19 | ) 20 | 21 | func TestMPInt(t *testing.T) { 22 | one := FromBytes(oneData) 23 | two := FromBytes(twoData) 24 | three := FromBytes(threeData) 25 | 26 | sum := Add(one, two) 27 | if sum.Cmp(three) != 0 { 28 | t.Errorf("%s + %s = %s, expected %s\n", one, two, sum, three) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ot/ot.go: -------------------------------------------------------------------------------- 1 | // 2 | // ot.go 3 | // 4 | // Copyright (c) 2023 Markku Rossi 5 | // 6 | // All rights reserved. 7 | 8 | package ot 9 | 10 | // OT defines Oblivious Transfer protocol. 11 | type OT interface { 12 | // InitSender initializes the OT sender. 13 | InitSender(io IO) error 14 | 15 | // InitReceiver initializes the OT receiver. 16 | InitReceiver(io IO) error 17 | 18 | // Send sends the wire labels with OT. 19 | Send(wires []Wire) error 20 | 21 | // Receive receives the wire labels with OT based on the flag values. 22 | Receive(flags []bool, result []Label) error 23 | } 24 | -------------------------------------------------------------------------------- /ot/pipe.go: -------------------------------------------------------------------------------- 1 | // 2 | // pipe.go 3 | // 4 | // Copyright (c) 2023-2024 Markku Rossi 5 | // 6 | // All rights reserved. 7 | 8 | package ot 9 | 10 | import ( 11 | "fmt" 12 | "io" 13 | ) 14 | 15 | var ( 16 | _ IO = &Pipe{} 17 | ) 18 | 19 | // Pipe implements the IO interface with in-memory io.Pipe. 20 | type Pipe struct { 21 | rBuf []byte 22 | wBuf []byte 23 | r *io.PipeReader 24 | w *io.PipeWriter 25 | } 26 | 27 | // NewPipe creates a new in-memory pipe. 28 | func NewPipe() (*Pipe, *Pipe) { 29 | ar, aw := io.Pipe() 30 | br, bw := io.Pipe() 31 | 32 | return &Pipe{ 33 | rBuf: make([]byte, 64*1024), 34 | wBuf: make([]byte, 64*1024), 35 | r: ar, 36 | w: bw, 37 | }, &Pipe{ 38 | rBuf: make([]byte, 64*1024), 39 | wBuf: make([]byte, 64*1024), 40 | r: br, 41 | w: aw, 42 | } 43 | } 44 | 45 | // SendByte sends a byte value. 46 | func (p *Pipe) SendByte(val byte) error { 47 | p.wBuf[0] = val 48 | _, err := p.w.Write(p.wBuf[:1]) 49 | return err 50 | } 51 | 52 | // SendUint32 sends an uint32 value. 53 | func (p *Pipe) SendUint32(val int) error { 54 | bo.PutUint32(p.wBuf, uint32(val)) 55 | _, err := p.w.Write(p.wBuf[:4]) 56 | return err 57 | } 58 | 59 | // SendData sends binary data. 60 | func (p *Pipe) SendData(val []byte) error { 61 | l := len(val) 62 | bo.PutUint32(p.wBuf, uint32(l)) 63 | n := copy(p.wBuf[4:], val) 64 | if n != l { 65 | return fmt.Errorf("pipe buffer too short: %d > %d", l, len(p.wBuf)) 66 | } 67 | _, err := p.w.Write(p.wBuf[:4+l]) 68 | return err 69 | } 70 | 71 | // Flush flushed any pending data in the connection. 72 | func (p *Pipe) Flush() error { 73 | return nil 74 | } 75 | 76 | // Drain consumes all input from the pipe. 77 | func (p *Pipe) Drain() error { 78 | _, err := io.Copy(io.Discard, p.r) 79 | return err 80 | } 81 | 82 | // Close closes the pipe. 83 | func (p *Pipe) Close() error { 84 | return p.w.Close() 85 | } 86 | 87 | // ReceiveByte receives a byte value. 88 | func (p *Pipe) ReceiveByte() (byte, error) { 89 | _, err := p.r.Read(p.rBuf[:1]) 90 | if err != nil { 91 | return 0, err 92 | } 93 | return p.rBuf[0], nil 94 | } 95 | 96 | // ReceiveUint32 receives an uint32 value. 97 | func (p *Pipe) ReceiveUint32() (int, error) { 98 | _, err := p.r.Read(p.rBuf[:4]) 99 | if err != nil { 100 | return 0, err 101 | } 102 | return int(bo.Uint32(p.rBuf)), nil 103 | } 104 | 105 | // ReceiveData receives binary data. 106 | func (p *Pipe) ReceiveData() ([]byte, error) { 107 | _, err := p.r.Read(p.rBuf[:4]) 108 | if err != nil { 109 | return nil, err 110 | } 111 | l := bo.Uint32(p.rBuf) 112 | if l > uint32(len(p.rBuf)) { 113 | return nil, fmt.Errorf("pipe buffer too short: %d > %d", l, len(p.rBuf)) 114 | } 115 | n, err := p.r.Read(p.rBuf[:]) 116 | return p.rBuf[:n], err 117 | } 118 | -------------------------------------------------------------------------------- /ot/pipe_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // pipe_test.go 3 | // 4 | // Copyright (c) 2023-2024 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package ot 10 | 11 | import ( 12 | "bytes" 13 | "fmt" 14 | "io" 15 | "testing" 16 | ) 17 | 18 | func TestPipe(t *testing.T) { 19 | var tests = []interface{}{ 20 | byte('@'), 21 | 42, 22 | []byte("Hello, world!"), 23 | } 24 | 25 | pipe, rPipe := NewPipe() 26 | done := make(chan error) 27 | 28 | go func(pipe *Pipe) { 29 | for _, test := range tests { 30 | switch v := test.(type) { 31 | case byte: 32 | val, err := pipe.ReceiveByte() 33 | if err != nil { 34 | done <- err 35 | pipe.Close() 36 | return 37 | } 38 | if val != v { 39 | done <- fmt.Errorf("ReceiveByte: mismatch: %v != %v", 40 | val, v) 41 | pipe.Close() 42 | return 43 | } 44 | 45 | case int: 46 | val, err := pipe.ReceiveUint32() 47 | if err != nil { 48 | done <- err 49 | pipe.Close() 50 | return 51 | } 52 | if val != v { 53 | done <- fmt.Errorf("ReceiveUint32: mismatch: %v != %v", 54 | val, v) 55 | pipe.Close() 56 | return 57 | } 58 | 59 | case []byte: 60 | data, err := pipe.ReceiveData() 61 | if err != nil { 62 | done <- err 63 | pipe.Close() 64 | return 65 | } 66 | if bytes.Compare(data, v) != 0 { 67 | done <- fmt.Errorf("ReceiveData: mismatch: %x != %x", 68 | data, v) 69 | pipe.Close() 70 | return 71 | } 72 | 73 | default: 74 | panic(fmt.Sprintf("receive %v(%T) not supported", v, v)) 75 | } 76 | } 77 | _, err := pipe.ReceiveUint32() 78 | if err != io.EOF { 79 | done <- fmt.Errorf("expected EOF") 80 | } 81 | done <- nil 82 | }(rPipe) 83 | 84 | for _, test := range tests { 85 | switch v := test.(type) { 86 | case byte: 87 | err := pipe.SendByte(v) 88 | if err != nil { 89 | t.Errorf("SendByte failed: %v", err) 90 | } 91 | 92 | case int: 93 | err := pipe.SendUint32(v) 94 | if err != nil { 95 | t.Errorf("SendUint32 failed: %v", err) 96 | } 97 | 98 | case []byte: 99 | err := pipe.SendData(v) 100 | if err != nil { 101 | t.Errorf("SendData failed: %v", err) 102 | } 103 | } 104 | } 105 | err := pipe.Close() 106 | if err != nil { 107 | t.Errorf("Close failed: %v", err) 108 | } 109 | 110 | err = <-done 111 | if err != nil { 112 | t.Errorf("consumer failed: %v", err) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /ot/rsa_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // rsa_test.go 3 | // 4 | // Copyright (c) 2019-2023 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package ot 10 | 11 | import ( 12 | "bytes" 13 | "crypto/rand" 14 | "testing" 15 | ) 16 | 17 | func benchmark(b *testing.B, keySize int) { 18 | l0, _ := NewLabel(rand.Reader) 19 | l1, _ := NewLabel(rand.Reader) 20 | 21 | sender, err := NewSender(keySize) 22 | if err != nil { 23 | b.Fatal(err) 24 | } 25 | 26 | receiver, err := NewReceiver(sender.PublicKey()) 27 | if err != nil { 28 | b.Fatal(err) 29 | } 30 | 31 | b.ResetTimer() 32 | 33 | var l0Buf, l1Buf LabelData 34 | for i := 0; i < b.N; i++ { 35 | l0Data := l0.Bytes(&l0Buf) 36 | l1Data := l1.Bytes(&l1Buf) 37 | sXfer, err := sender.NewTransfer(l0Data, l1Data) 38 | if err != nil { 39 | b.Fatal(err) 40 | } 41 | rXfer, err := receiver.NewTransfer(1) 42 | if err != nil { 43 | b.Fatal(err) 44 | } 45 | err = rXfer.ReceiveRandomMessages(sXfer.RandomMessages()) 46 | if err != nil { 47 | b.Fatal(err) 48 | } 49 | 50 | sXfer.ReceiveV(rXfer.V()) 51 | err = rXfer.ReceiveMessages(sXfer.Messages()) 52 | if err != nil { 53 | b.Fatal(err) 54 | } 55 | 56 | m, bit := rXfer.Message() 57 | var ret int 58 | if bit == 0 { 59 | ret = bytes.Compare(l0Data[:], m) 60 | } else { 61 | ret = bytes.Compare(l1Data[:], m) 62 | } 63 | if ret != 0 { 64 | b.Fatal("Verify failed!\n") 65 | } 66 | } 67 | } 68 | 69 | func BenchmarkRSA512(b *testing.B) { 70 | benchmark(b, 512) 71 | } 72 | 73 | func BenchmarkRSA1024(b *testing.B) { 74 | benchmark(b, 1024) 75 | } 76 | 77 | func BenchmarkRSA2048(b *testing.B) { 78 | benchmark(b, 2048) 79 | } 80 | -------------------------------------------------------------------------------- /pkg/README.md: -------------------------------------------------------------------------------- 1 | # 3rd Party Files 2 | 3 | ## SCALE-MAMBA Software 4 | 5 | The following circuit files are from the [SCALE-MAMBA Software](https://github.com/KULeuven-COSIC/SCALE-MAMBA): 6 | 7 | - [crypto/aes/aes_128.circ](crypto/aes/aes_128.circ) 8 | - [crypto/aes/aes_192.circ](crypto/aes/aes_192.circ) 9 | - [crypto/aes/aes_256.circ](crypto/aes/aes_256.circ) 10 | - [crypto/sha256/sha256.circ](crypto/sha256/sha256.circ) 11 | - [crypto/sha512/sha512.circ](crypto/sha512/sha512.circ) 12 | - [math/add64.circ](math/add64.circ) 13 | - [math/div64.circ](math/div64.circ) 14 | - [math/mul64.circ](math/mul64.circ) 15 | - [math/sub64.circ](math/sub64.circ) 16 | 17 | The SCALE-MAMBA license is as follows: 18 | 19 | ``` 20 | This software incorporates components from the original SPDZ system from the University 21 | of Bristol, as well as various extensions. The scasm assembler and wasca system for 22 | compiling Rust programs is a co-development between KU Leuven and Cosmian 23 | 24 | The copyright therefore rests with the following three institutions: 25 | 26 | Copyright (c) 2017, The University of Bristol, Senate House, Tyndall Avenue, Bristol, BS8 1TH, United Kingdom. 27 | Copyright (c) 2021, imec-COSIC, KU Leuven, Kasteelpark Arenberg 10, bus 2452, B-3001 Leuven-Heverlee, Belgium. 28 | Copyright (c) 2021, Copyright (c) 2021, Cosmian Tech SAS, 53-55 rue La Boétie, Paris, France. 29 | 30 | All rights reserved 31 | 32 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 33 | 34 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 35 | 36 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 37 | 38 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 39 | 40 | 41 | It would be helpful if any use of the software for commercial purposes is reported to the nigel.smart@kuleuven.be 42 | This is for impact and usage monitoring purposes only. 43 | 44 | Enquiries about further applications and development opportunities are welcome. Please contact nigel.smart@kuleuven.be 45 | ``` 46 | -------------------------------------------------------------------------------- /pkg/bytes/bytes.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2023 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | package bytes 9 | 10 | // Compare compares two byte slices lexicographically. The result is 0 11 | // if a == b, -1 if a < b, and +1 if a > b. 12 | func Compare(a, b []byte) int { 13 | limit := len(a) 14 | if len(b) < limit { 15 | limit = len(b) 16 | } 17 | for i := 0; i < limit; i++ { 18 | if a[i] < b[i] { 19 | return -1 20 | } 21 | if a[i] > b[i] { 22 | return 1 23 | } 24 | } 25 | if len(a) < len(b) { 26 | return -1 27 | } 28 | if len(a) > len(b) { 29 | return 1 30 | } 31 | return 0 32 | } 33 | -------------------------------------------------------------------------------- /pkg/bytes/doc.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2023 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | // Package bytes implements functions for byte slice manipulation. 9 | package bytes 10 | -------------------------------------------------------------------------------- /pkg/crypto/aes/cipher.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2020-2021 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | // Package aes implements the Advanced Encryption Standard (AES) 9 | // block cipher operations. 10 | package aes 11 | 12 | // BlockSize defines the AES cipher block size in bytes. 13 | const BlockSize = 16 14 | 15 | // EncryptBlock encrypts one data block with the key. The key must be 16 | // 16, 24, or 32 bytes long. 17 | func EncryptBlock(key []byte, data [BlockSize]byte) [BlockSize]byte { 18 | enc := ExpandEncryptionKey(key) 19 | 20 | var dst [BlockSize]byte 21 | 22 | return EncryptBlockExpanded(enc, dst, data) 23 | } 24 | 25 | // DecryptBlock decrypts one data block with the key. The key must be 26 | // 16, 24, or 32 bytes long. 27 | func DecryptBlock(key []byte, data [BlockSize]byte) [BlockSize]byte { 28 | enc, dec := ExpandKey(key) 29 | 30 | var dst [BlockSize]byte 31 | 32 | return DecryptBlockExpanded(dec, dst, data) 33 | } 34 | -------------------------------------------------------------------------------- /pkg/crypto/aes/circuit.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2020-2024 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | package aes 9 | 10 | // Block128 encrypts one data block with 128 bit key. 11 | func Block128(key [16]byte, data [16]byte) [16]byte { 12 | var k uint128 13 | var d uint128 14 | 15 | for i := 0; i < len(key); i++ { 16 | k <<= 8 17 | k |= uint128(key[i]) 18 | } 19 | for i := 0; i < len(data); i++ { 20 | d <<= 8 21 | d |= uint128(data[i]) 22 | } 23 | 24 | c := block128(k, d) 25 | var cipher [16]byte 26 | for i := len(cipher) - 1; i >= 0; i-- { 27 | cipher[i] = byte(c & 0xff) 28 | c >>= 8 29 | } 30 | return cipher 31 | } 32 | 33 | // Block192 encrypts one data block with 192 bit key. 34 | func Block192(key [24]byte, data [16]byte) [16]byte { 35 | var k uint192 36 | var d uint128 37 | 38 | for i := 0; i < len(key); i++ { 39 | k <<= 8 40 | k |= uint192(key[i]) 41 | } 42 | for i := 0; i < len(data); i++ { 43 | d <<= 8 44 | d |= uint128(data[i]) 45 | } 46 | 47 | c := block192(k, d) 48 | var cipher [16]byte 49 | for i := len(cipher) - 1; i >= 0; i-- { 50 | cipher[i] = c & 0xff 51 | c >>= 8 52 | } 53 | return cipher 54 | } 55 | 56 | // Block256 encrypts one data block with 256 bit key. 57 | func Block256(key [32]byte, data [16]byte) [16]byte { 58 | var k uint256 59 | var d uint128 60 | 61 | for i := 0; i < len(key); i++ { 62 | k <<= 8 63 | k |= uint256(key[i]) 64 | } 65 | for i := 0; i < len(data); i++ { 66 | d <<= 8 67 | d |= uint128(data[i]) 68 | } 69 | 70 | c := block256(k, d) 71 | var cipher [16]byte 72 | for i := len(cipher) - 1; i >= 0; i-- { 73 | cipher[i] = c & 0xff 74 | c >>= 8 75 | } 76 | return cipher 77 | } 78 | 79 | func block128(key uint128, block uint128) uint128 { 80 | return native("aes_128.circ", key, block) 81 | } 82 | 83 | func block192(key uint192, block uint128) uint128 { 84 | return native("aes_192.circ", key, block) 85 | } 86 | 87 | func block256(key uint256, block uint128) uint128 { 88 | return native("aes_256.circ", key, block) 89 | } 90 | -------------------------------------------------------------------------------- /pkg/crypto/cipher/cbc/cbc.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2021-2024 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | // Package cbc implements the cipher block chaining (CBC) mode of 9 | // operation for block ciphers. 10 | package cbc 11 | 12 | import ( 13 | "crypto/aes" 14 | ) 15 | 16 | // PadAES pads the data to AES cipher block boundary. The padding is 17 | // filled with byte value describing the padding length. Note that 18 | // data will be always padded. If the original data is already padded, 19 | // the padding will be one full AES block. 20 | func PadAES(data []byte) []byte { 21 | padLen := aes.BlockSize - len(data)%aes.BlockSize 22 | var padded [len(data) + padLen]byte 23 | 24 | for i := 0; i < padLen; i++ { 25 | padded[len(data)+i] = byte(padLen) 26 | } 27 | copy(padded, data) 28 | 29 | return padded 30 | } 31 | 32 | // EncryptAES128 encrypts the data in AES-CBC mode. The key specifies 33 | // the AES encryption key and iv is a random initialization 34 | // vector. The data must be padded to AES block size. 35 | // 36 | // // Case #1: Encrypting 16 bytes (1 block) using AES-CBC with 128-bit key 37 | // // Key : 0x06a9214036b8a15b512e03d534120006 38 | // // IV : 0x3dafba429d9eb430b422da802c9fac41 39 | // // Plaintext : "Single block msg" 40 | // // Ciphertext: 0xe353779c1079aeb82708942dbe77181a 41 | // 42 | // key := []byte{ 43 | // 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b, 44 | // 0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06, 45 | // } 46 | // iv := []byte{ 47 | // 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30, 48 | // 0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41, 49 | // } 50 | // plain := []byte("Single block msg") 51 | // 52 | // cipher := cbc.EncryptAES128(key, iv, plain) 53 | // => e353779c1079aeb82708942dbe77181a 54 | func EncryptAES128(key [16]byte, iv [aes.BlockSize]byte, data []byte) []byte { 55 | var block [aes.BlockSize]byte 56 | copy(block, iv) 57 | 58 | var plain [aes.BlockSize]byte 59 | var cipher [len(data)]byte 60 | 61 | for i := 0; i < len(data)/aes.BlockSize; i++ { 62 | copy(plain, data[i*aes.BlockSize:]) 63 | for j := 0; j < aes.BlockSize; j++ { 64 | plain[j] ^= block[j] 65 | } 66 | //block = aes.Block128(key, plain) 67 | block = aes.EncryptBlock(key, plain) 68 | copy(cipher[i*aes.BlockSize:], block) 69 | } 70 | 71 | return cipher 72 | } 73 | 74 | func DecryptAES128(key [16]byte, iv [aes.BlockSize]byte, data []byte) []byte { 75 | var block [aes.BlockSize]byte 76 | 77 | var cipher [aes.BlockSize]byte 78 | var plain [len(data)]byte 79 | 80 | for i := 0; i < len(data)/aes.BlockSize; i++ { 81 | copy(cipher, data[i*aes.BlockSize:]) 82 | block = aes.DecryptBlock(key, cipher) 83 | for j := 0; j < aes.BlockSize; j++ { 84 | block[j] ^= iv[j] 85 | } 86 | copy(plain[i*aes.BlockSize:], block) 87 | copy(iv, cipher) 88 | } 89 | 90 | return plain 91 | } 92 | -------------------------------------------------------------------------------- /pkg/crypto/curve25519/doc.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2023 Markku Rossi 4 | // 5 | // Curve25519 algorithm in MPCL. This file is derived from the 6 | // internal/x/crypto/curve25519 package of Go 1.12.17. The original 7 | // copyright notice is as follows: 8 | // 9 | // Copyright 2012 The Go Authors. All rights reserved. 10 | // Use of this source code is governed by a BSD-style 11 | // license that can be found in the LICENSE file. 12 | 13 | // Package curve25519 provides an implementation of scalar multiplication on 14 | // the elliptic curve known as curve25519. See https://cr.yp.to/ecdh.html 15 | package curve25519 16 | 17 | // basePoint is the x coordinate of the generator of the curve. 18 | var basePoint = [32]byte{9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} 19 | 20 | // ScalarMult sets dst to the product in*base where dst and base are the x 21 | // coordinates of group points and all values are in little-endian form. 22 | func ScalarMult(dst, in, base *[32]byte) { 23 | scalarMult(dst, in, base) 24 | } 25 | 26 | // ScalarBaseMult sets dst to the product in*base where dst and base are the x 27 | // coordinates of group points, base is the standard generator and all values 28 | // are in little-endian form. 29 | func ScalarBaseMult(dst, in *[32]byte) { 30 | ScalarMult(dst, in, &basePoint) 31 | } 32 | -------------------------------------------------------------------------------- /pkg/crypto/ed25519/README.md: -------------------------------------------------------------------------------- 1 | * Ed25519 2 | -------------------------------------------------------------------------------- /pkg/crypto/ed25519/keygen.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2021-2024 Markku Rossi 4 | // 5 | // Ed25519 key generation in MPCL. This file is derived from Go's 6 | // crypto/ed25519 package. The original copyright notice follows: 7 | // 8 | // Copyright 2016 The Go Authors. All rights reserved. 9 | // Use of this source code is governed by a BSD-style 10 | // license that can be found in the LICENSE file. 11 | 12 | // Package ed25519 implements the Ed25519 signature algorithm. 13 | package ed25519 14 | 15 | import ( 16 | "crypto/ed25519/internal/edwards25519" 17 | "crypto/sha512" 18 | ) 19 | 20 | // NewKeyFromSeed calculates a private key and a public key from a 21 | // seed. RFC 8032's private keys correspond to seeds in this package. 22 | func NewKeyFromSeed(seed [SeedSize]byte) (PublicKey, PrivateKey) { 23 | digest := sha512.Sum512(seed) 24 | digest[0] &= 248 25 | digest[31] &= 127 26 | digest[31] |= 64 27 | 28 | var A edwards25519.ExtendedGroupElement 29 | var hBytes [32]byte 30 | copy(hBytes, digest) 31 | edwards25519.GeScalarMultBase(&A, &hBytes) 32 | var publicKeyBytes [32]byte 33 | A.ToBytes(&publicKeyBytes) 34 | 35 | var privateKey [64]byte 36 | copy(privateKey, seed) 37 | copy(privateKey[32:], publicKeyBytes) 38 | 39 | return publicKeyBytes, privateKey 40 | } 41 | -------------------------------------------------------------------------------- /pkg/crypto/ed25519/sign.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2021-2024 Markku Rossi 4 | // 5 | // Ed25519 signature computation in MPCL. This file is derived from 6 | // Go's crypto/ed25519 package. The original copyright notice follows: 7 | // 8 | // Copyright 2016 The Go Authors. All rights reserved. 9 | // Use of this source code is governed by a BSD-style 10 | // license that can be found in the LICENSE file. 11 | 12 | package ed25519 13 | 14 | import ( 15 | "crypto/ed25519/internal/edwards25519" 16 | "crypto/sha512" 17 | ) 18 | 19 | const ( 20 | // PublicKeySize is the size, in bytes, of public keys as used in 21 | // this package. 22 | PublicKeySize = 32 23 | // PrivateKeySize is the size, in bytes, of private keys as used 24 | // in this package. 25 | PrivateKeySize = 64 26 | // SignatureSize is the size, in bytes, of signatures generated 27 | // and verified by this package. 28 | SignatureSize = 64 29 | // SeedSize is the size, in bytes, of private key seeds. These are 30 | // the private key representations used by RFC 8032. 31 | SeedSize = 32 32 | ) 33 | 34 | // PrivateKey defines the Ed25519 private key. 35 | type PrivateKey [PrivateKeySize]byte 36 | 37 | // PublicKey defines the Ed25519 public key. 38 | type PublicKey [PublicKeySize]byte 39 | 40 | // Sign signs the message with privateKey and returns the signature. 41 | func Sign(privateKey PrivateKey, message []byte) []byte { 42 | 43 | digest1 := sha512.Sum512(privateKey[0:32]) 44 | 45 | var expandedSecretKey [32]byte 46 | copy(expandedSecretKey, digest1) 47 | expandedSecretKey[0] &= 248 48 | expandedSecretKey[31] &= 63 49 | expandedSecretKey[31] |= 64 50 | 51 | buf := make([]byte, 32+len(message)) 52 | copy(buf, digest1[32:]) 53 | copy(buf[32:], message) 54 | messageDigest := sha512.Sum512(buf) 55 | 56 | var messageDigestReduced [32]byte 57 | edwards25519.ScReduce(&messageDigestReduced, messageDigest) 58 | var R edwards25519.ExtendedGroupElement 59 | edwards25519.GeScalarMultBase(&R, &messageDigestReduced) 60 | 61 | var encodedR [32]byte 62 | R.ToBytes(&encodedR) 63 | 64 | buf2 := make([]byte, 64+len(message)) 65 | copy(buf2, encodedR) 66 | copy(buf2[32:], privateKey[32:]) 67 | copy(buf2[64:], message) 68 | hramDigest := sha512.Sum512(buf2) 69 | 70 | var hramDigestReduced [32]byte 71 | edwards25519.ScReduce(&hramDigestReduced, hramDigest) 72 | 73 | var s [32]byte 74 | edwards25519.ScMulAdd(&s, hramDigestReduced, expandedSecretKey, 75 | messageDigestReduced) 76 | 77 | var signature [SignatureSize]byte 78 | copy(signature, encodedR) 79 | copy(signature[32:], s) 80 | 81 | return signature 82 | } 83 | -------------------------------------------------------------------------------- /pkg/crypto/hmac/doc.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2021-2024 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | // Package hmac implements Keyed-Hash Message Authentication Code 9 | // (HMAC) functions as defined in RFC 2104. All functions take the 10 | // data that is authenticated and an authentication key. 11 | // 12 | // signature := hmac.SumSHA256([]byte("message"), []byte("abc")) 13 | // => 859cc656e12c0ecd0afdd7e3d034c3ee81609fcac1b454c231211c7ac69895e8 14 | package hmac 15 | -------------------------------------------------------------------------------- /pkg/crypto/hmac/sha1.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2020-2024 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | package hmac 9 | 10 | import ( 11 | "crypto/sha1" 12 | ) 13 | 14 | // SumSHA1 computes the HMAC-SHA1 signature for the data using the 15 | // key. 16 | func SumSHA1(data, key []byte) [sha1.Size]byte { 17 | if len(key) > sha1.BlockSize { 18 | key = sha1.Sum(key[:]) 19 | } 20 | 21 | var ipad [sha1.BlockSize]byte 22 | var opad [sha1.BlockSize]byte 23 | 24 | copy(ipad, key) 25 | copy(opad, key) 26 | 27 | for i := 0; i < len(ipad); i++ { 28 | ipad[i] ^= 0x36 29 | } 30 | for i := 0; i < len(opad); i++ { 31 | opad[i] ^= 0x5c 32 | } 33 | 34 | var idata [len(ipad) + len(data)]byte 35 | copy(idata, ipad) 36 | copy(idata[len(ipad):], data) 37 | 38 | idigest := sha1.Sum(idata[:]) 39 | 40 | var odata [len(opad) + len(idigest)]byte 41 | copy(odata, opad) 42 | copy(odata[len(opad):], idigest) 43 | 44 | return sha1.Sum(odata[:]) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/crypto/hmac/sha256.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2020-2024 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | package hmac 9 | 10 | import ( 11 | "crypto/sha256" 12 | ) 13 | 14 | // SumSHA256 computes the HMAC-SHA256 signature for the data using the 15 | // key. 16 | func SumSHA256(data, key []byte) [sha256.Size]byte { 17 | if len(key) > sha256.BlockSize { 18 | key = sha256.Sum256(key[:]) 19 | } 20 | 21 | var ipad [sha256.BlockSize]byte 22 | var opad [sha256.BlockSize]byte 23 | 24 | copy(ipad, key) 25 | copy(opad, key) 26 | 27 | for i := 0; i < len(ipad); i++ { 28 | ipad[i] ^= 0x36 29 | } 30 | for i := 0; i < len(opad); i++ { 31 | opad[i] ^= 0x5c 32 | } 33 | 34 | var idata [len(ipad) + len(data)]byte 35 | copy(idata, ipad) 36 | copy(idata[len(ipad):], data) 37 | 38 | idigest := sha256.Sum256(idata[:]) 39 | 40 | var odata [len(opad) + len(idigest)]byte 41 | copy(odata, opad) 42 | copy(odata[len(opad):], idigest) 43 | 44 | return sha256.Sum256(odata[:]) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/crypto/hmac/sha512.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2020-2024 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | package hmac 9 | 10 | import ( 11 | "crypto/sha512" 12 | ) 13 | 14 | // SumSHA512 computes the HMAC-SHA512 signature for the data using the 15 | // key. 16 | func SumSHA512(data, key []byte) [sha512.Size]byte { 17 | if len(key) > sha512.BlockSize { 18 | key = sha512.Sum512(key[:]) 19 | } 20 | 21 | var ipad [sha512.BlockSize]byte 22 | var opad [sha512.BlockSize]byte 23 | 24 | copy(ipad, key) 25 | copy(opad, key) 26 | 27 | for i := 0; i < len(ipad); i++ { 28 | ipad[i] ^= 0x36 29 | } 30 | for i := 0; i < len(opad); i++ { 31 | opad[i] ^= 0x5c 32 | } 33 | 34 | var idata [len(ipad) + len(data)]byte 35 | copy(idata, ipad) 36 | copy(idata[len(ipad):], data) 37 | 38 | idigest := sha512.Sum512(idata[:]) 39 | 40 | var odata [len(opad) + len(idigest)]byte 41 | copy(odata, opad) 42 | copy(odata[len(opad):], idigest) 43 | 44 | return sha512.Sum512(odata[:]) 45 | } 46 | -------------------------------------------------------------------------------- /pkg/crypto/rsa/rsa.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2020-2021 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | // Package rsa implements RSA encrypt and decrypt operations. The 9 | // practical key sizes are <= 512 bits i.e. this must not be used in 10 | // any real life applications. 11 | package rsa 12 | 13 | import ( 14 | "math" 15 | ) 16 | 17 | const ( 18 | // E65537 is the public RSA exponent 2^16+1. 19 | E65537 = 0x10001 20 | ) 21 | 22 | // Encrypt encrypts the message with the public key {e, n}. 23 | // cipher = msg**e mod n 24 | func Encrypt(msg, e, n uint) uint { 25 | return math.Exp(msg, e, n) 26 | } 27 | 28 | // Decrypt decrypts the cipher text with the private key {d, n}. 29 | // message = cipher**d mod n 30 | func Decrypt(cipher, d, n uint) uint { 31 | return math.Exp(cipher, d, n) 32 | } 33 | -------------------------------------------------------------------------------- /pkg/crypto/sha1/sha1.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2024 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | // SHA-1 algorithm in MPCL. This file is derived form the 8 | // src/crypto/sha1/sha1.go file of Go 1.22.4. The original copyright 9 | // notice is as follows: 10 | // 11 | // Copyright 2009 The Go Authors. All rights reserved. 12 | // Use of this source code is governed by a BSD-style 13 | // license that can be found in the LICENSE file. 14 | 15 | // Package sha1 implements the SHA-1 hash algorithm as defined in RFC 3174. 16 | // 17 | // SHA-1 is cryptographically broken and should not be used for secure 18 | // applications. 19 | package sha1 20 | 21 | import ( 22 | "encoding/binary" 23 | ) 24 | 25 | // The size of a SHA-1 checksum in bytes. 26 | const Size = 20 27 | 28 | // The blocksize of SHA-1 in bytes. 29 | const BlockSize = 64 30 | 31 | const ( 32 | chunk = 64 33 | init0 = 0x67452301 34 | init1 = 0xEFCDAB89 35 | init2 = 0x98BADCFE 36 | init3 = 0x10325476 37 | init4 = 0xC3D2E1F0 38 | ) 39 | 40 | // Sum returns the SHA-1 checksum of the data. 41 | func Sum(data []byte) [Size]byte { 42 | state := [5]uint32{init0, init1, init2, init3, init4} 43 | length := len(data) 44 | 45 | for len(data) >= chunk { 46 | state = Block(data[:chunk], state) 47 | data = data[chunk:] 48 | } 49 | 50 | // Padding. Add a 1 bit and 0 bits until 56 bytes mod 64. 51 | 52 | var tmp [128 + 8]byte // padding + length buffer 53 | for idx, d := range data { 54 | tmp[idx] = d 55 | } 56 | i := len(data) 57 | tmp[i] = 0x80 58 | 59 | var t int32 60 | if length%64 < 56 { 61 | t = 56 - length%64 62 | } else { 63 | t = 64 + 56 - length%64 64 | } 65 | t += i 66 | 67 | // Length in bits. 68 | length <<= 3 69 | padlen := tmp[:t+8] 70 | padlen = binary.PutUint64(padlen, t, length) 71 | 72 | for len(padlen) >= chunk { 73 | state = Block(padlen[:chunk], state) 74 | padlen = padlen[chunk:] 75 | } 76 | 77 | var digest [Size]byte 78 | 79 | digest = binary.PutUint32(digest, 0, state[0]) 80 | digest = binary.PutUint32(digest, 4, state[1]) 81 | digest = binary.PutUint32(digest, 8, state[2]) 82 | digest = binary.PutUint32(digest, 12, state[3]) 83 | digest = binary.PutUint32(digest, 16, state[4]) 84 | 85 | return digest 86 | } 87 | -------------------------------------------------------------------------------- /pkg/crypto/sha256/sum.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2020-2021 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | // Package sha256 implements the he SHA-224 and SHA-256 cryptographic 9 | // hash functions. 10 | package sha256 11 | 12 | const ( 13 | // The size of a SHA256 checksum in bytes. 14 | Size = 32 15 | 16 | // The blocksize of SHA256 and SHA224 in bytes. 17 | BlockSize = 64 18 | 19 | init = 0x6a09e667bb67ae853c6ef372a54ff53a510e527f9b05688c1f83d9ab5be0cd19 20 | ) 21 | 22 | // Sum256 returns the SHA256 checksum of the data. 23 | func Sum256(data []byte) [Size]byte { 24 | var state uint256 = init 25 | var block uint512 26 | var hash [Size]byte 27 | 28 | var pad [BlockSize]byte 29 | pad[0] = 0x80 30 | 31 | for i := 0; i < len(data); i++ { 32 | block <<= 8 33 | block = block | uint512(data[i]) 34 | 35 | if (i+1)%BlockSize == 0 { 36 | state = Block(block, state) 37 | block = uint512(0) 38 | } 39 | } 40 | if len(data)%BlockSize < 56 { 41 | for i := len(data) % BlockSize; i < 56; i++ { 42 | block <<= 8 43 | block |= uint512(pad[i-len(data)%BlockSize]) 44 | } 45 | } else { 46 | for i := len(data) % BlockSize; i < BlockSize; i++ { 47 | block <<= 8 48 | block |= uint512(pad[i-len(data)%BlockSize]) 49 | } 50 | state = Block(block, state) 51 | block = uint512(0) 52 | } 53 | // Length in bits. 54 | block <<= 64 55 | block |= uint512(len(data) << 3) 56 | 57 | state = Block(block, state) 58 | 59 | for i := 0; i < Size; i++ { 60 | hash[Size-i-1] = byte(state & 0xff) 61 | state >>= 8 62 | } 63 | return hash 64 | } 65 | 66 | // Block adds a new SHA-256 block to the state. 67 | func Block(block uint512, state uint256) uint256 { 68 | return native("sha256.circ", block, state) 69 | } 70 | -------------------------------------------------------------------------------- /pkg/crypto/sha512/sha512.mpclc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markkurossi/mpc/8a4e258ee2b98a9e96e8f18193a6813dc3281d36/pkg/crypto/sha512/sha512.mpclc -------------------------------------------------------------------------------- /pkg/crypto/sha512/sum.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2020-2023 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | // Package sha512 implements the SHA-384, SHA-512, SHA-512/224, and 9 | // SHA-512/256 cryptographic hash functions. 10 | package sha512 11 | 12 | const ( 13 | // The size of a SHA512 checksum in bytes. 14 | Size = 64 15 | 16 | // BlockSize is the block size, in bytes, of the SHA-512/224, 17 | // SHA-512/256, SHA-384 and SHA-512 hash functions. 18 | BlockSize = 128 19 | 20 | init = 0x6a09e667f3bcc908bb67ae8584caa73b3c6ef372fe94f82ba54ff53a5f1d36f1510e527fade682d19b05688c2b3e6c1f1f83d9abfb41bd6b5be0cd19137e2179 21 | ) 22 | 23 | // Sum512 returns the SHA512 checksum of the data. 24 | func Sum512(data []byte) [Size]byte { 25 | var state uint512 = init 26 | var block uint1024 27 | var hash [Size]byte 28 | 29 | var pad [BlockSize]byte 30 | pad[0] = 0x80 31 | 32 | for i := 0; i < len(data); i++ { 33 | block <<= 8 34 | block = block | uint1024(data[i]) 35 | 36 | if (i+1)%BlockSize == 0 { 37 | state = Block(block, state) 38 | block = 0 39 | } 40 | } 41 | if len(data)%BlockSize < 112 { 42 | for i := len(data) % BlockSize; i < 112; i++ { 43 | block <<= 8 44 | block |= uint1024(pad[i-len(data)%BlockSize]) 45 | } 46 | } else { 47 | for i := len(data) % BlockSize; i < BlockSize; i++ { 48 | block <<= 8 49 | block |= uint1024(pad[i-len(data)%BlockSize]) 50 | } 51 | state = Block(block, state) 52 | block = 0 53 | } 54 | // Length in bits. 55 | block <<= 128 56 | block |= uint1024(len(data) << 3) 57 | 58 | state = Block(block, state) 59 | 60 | for i := 0; i < Size; i++ { 61 | hash[Size-i-1] = byte(state & 0xff) 62 | state >>= 8 63 | } 64 | return hash 65 | } 66 | 67 | // Block adds a new SHA-512 block to the state. 68 | func Block(block uint1024, state uint512) uint512 { 69 | return native("sha512.mpclc", block, state) 70 | } 71 | -------------------------------------------------------------------------------- /pkg/encoding/binary/doc.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2021 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | // Package binary implements functions for processing binary data. 9 | package binary 10 | -------------------------------------------------------------------------------- /pkg/encoding/binary/getput.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2021-2024 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | package binary 9 | 10 | // GetUint gets a MSB-encoded unsigned integer from the argument 11 | // buffer. The size of the result number is determined by the length 12 | // of the input buffer. 13 | func GetUint(d []byte) uint { 14 | resultType := make(uint, len(d)*8) 15 | 16 | var result resultType 17 | for i := 0; i < len(d); i++ { 18 | result <<= 8 19 | result |= resultType(d[i]) 20 | } 21 | return result 22 | } 23 | 24 | // PutUint puts the unsigned integer v to the buffer d starting from 25 | // the offset offset in MSB-order. The number of bytes encoded is 26 | // determined by the size of the input value v. 27 | func PutUint(d []byte, offset int, v uint) []byte { 28 | bytes := size(v) / 8 29 | 30 | for i := 0; i < bytes; i++ { 31 | d[offset+bytes-1-i] = byte(v & 0xff) 32 | v >>= 8 33 | } 34 | return d 35 | } 36 | 37 | // GetUint32 gets a MSB-encoded uint32 value from the argument buffer. 38 | func GetUint32(d []byte) uint32 { 39 | return uint32(d[0])<<24 | uint32(d[1])<<16 | uint32(d[2])<<8 | uint32(d[3]) 40 | } 41 | 42 | // PutUint32 puts the uint32 value v to the buffer d starting from the 43 | // offset offset in MSB-order. 44 | func PutUint32(d []byte, offset int, v uint32) []byte { 45 | d[offset+0] = byte(v >> 24) 46 | d[offset+1] = byte(v >> 16) 47 | d[offset+2] = byte(v >> 8) 48 | d[offset+3] = byte(v) 49 | return d 50 | } 51 | 52 | // GetUint32 gets a LSB-encoded uint32 value from the argument buffer. 53 | func GetUint32LSB(d []byte) uint32 { 54 | return uint32(d[0]) | uint32(d[1])<<8 | uint32(d[2])<<16 | uint32(d[3])<<24 55 | } 56 | 57 | // GetUint64 gets a MSB-encoded uint64 value from the argument buffer. 58 | func GetUint64(d []byte) uint64 { 59 | return uint64(d[0])<<56 | uint64(d[1])<<48 | 60 | uint64(d[2])<<40 | uint64(d[3])<<32 | 61 | uint64(d[4])<<24 | uint64(d[5])<<16 | 62 | uint64(d[6])<<8 | uint64(d[7]) 63 | } 64 | 65 | // PutUint64 puts the uint64 value v to the buffer d starting from the 66 | // offset offset in MSB-order. 67 | func PutUint64(d []byte, offset int, v uint64) []byte { 68 | d[offset+0] = byte(v >> 56) 69 | d[offset+1] = byte(v >> 48) 70 | d[offset+2] = byte(v >> 40) 71 | d[offset+3] = byte(v >> 32) 72 | d[offset+4] = byte(v >> 24) 73 | d[offset+5] = byte(v >> 16) 74 | d[offset+6] = byte(v >> 8) 75 | d[offset+7] = byte(v) 76 | return d 77 | } 78 | 79 | // GetUint64LSB gets a LSB-encoded uint64 value from the argument buffer. 80 | func GetUint64LSB(d []byte) uint64 { 81 | return uint64(d[0]) | uint64(d[1])<<8 | 82 | uint64(d[2])<<16 | uint64(d[3])<<24 | 83 | uint64(d[4])<<32 | uint64(d[5])<<40 | 84 | uint64(d[6])<<48 | uint64(d[7])<<56 85 | } 86 | -------------------------------------------------------------------------------- /pkg/encoding/binary/metrics.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2020 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | package binary 9 | 10 | func HammingDistance(a, b uint) uint { 11 | return native("hamming", a, b) 12 | } 13 | -------------------------------------------------------------------------------- /pkg/encoding/hex/doc.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2023 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | // Package hex implements hexadecimal encoding and decoding functions. 9 | package hex 10 | -------------------------------------------------------------------------------- /pkg/encoding/hex/hex.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2023 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | package hex 9 | 10 | import ( 11 | "math" 12 | ) 13 | 14 | // Digits define the hexadecimal ASCII digits (0-9, a-f). 15 | var Digits = []byte{ 16 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 17 | 0x38, 0x39, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 18 | } 19 | 20 | // DecodeString decodes the bytes represented by the hexadecimal 21 | // string s. The function returns also a boolean success value. The 22 | // success value is false if the input string length is not even or if 23 | // any of the runes in the input string are not valid hexadecimal 24 | // digits (0-9, a-f, A-F). 25 | func DecodeString(s string) ([]byte, bool) { 26 | result := make([]byte, len(s)/2) 27 | 28 | if len(s)%2 != 0 { 29 | return result, false 30 | } 31 | var lo, hi int32 32 | for i := 0; i < len(s); i += 2 { 33 | hi = DigitToByte(rune(s[i])) 34 | if hi > 0xff { 35 | return result, false 36 | } 37 | lo = DigitToByte(rune(s[i+1])) 38 | if lo > 0xff { 39 | return result, false 40 | } 41 | result[i/2] = hi<<4 | lo 42 | } 43 | return result, true 44 | } 45 | 46 | // DigitToByte converts the hexadecimal digit r to its byte value. The 47 | // return value is math.MaxInt32 if the input digit is invalid. 48 | func DigitToByte(r rune) int32 { 49 | if '0' <= r && r <= '9' { 50 | return r - '0' 51 | } 52 | if 'a' <= r && r <= 'f' { 53 | return r - 'a' + 10 54 | } 55 | if 'A' <= r && r <= 'F' { 56 | return r - 'A' + 10 57 | } 58 | return math.MaxInt32 59 | } 60 | 61 | // EncodeToString returns a hexadecimal encoding of src. 62 | func EncodeToString(src []byte) string { 63 | bytes := make([]byte, len(src)*2) 64 | 65 | for i := 0; i < len(src); i++ { 66 | bytes[i*2] = Digits[src[i]>>4] 67 | bytes[i*2+1] = Digits[src[i]&0xf] 68 | } 69 | return string(bytes) 70 | } 71 | 72 | // EncodedLen returns the length of an encoding of n source 73 | // bytes. Specifically, this returns n * 2. 74 | func EncodedLen(n int) int { 75 | return n * 2 76 | } 77 | -------------------------------------------------------------------------------- /pkg/math/bits/bits.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2024 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | // Package bits implements bit manipulation functions for predefined 9 | // unsigned integer types. 10 | package bits 11 | 12 | // RotateLeft rotates x left by (k mod size(x)) bits. To rotate right 13 | // by k bits, call RotateLeft(x, -k). 14 | func RotateLeft(x uint, k int) uint { 15 | n := size(x) 16 | s := uint(k) & uint(n-1) 17 | return x<>(uint(n)-s) 18 | } 19 | 20 | // RotateLeft16 rotates x left by (k mod 16) bits. To rotate right by 21 | // k bits, call RotateLeft16(x, -k). 22 | func RotateLeft16(x uint16, k int) uint16 { 23 | n := 16 24 | s := uint(k) & uint(n-1) 25 | return x<>(uint(n)-s) 26 | } 27 | 28 | // RotateLeft32 rotates x left by (k mod 32) bits. To rotate right by 29 | // k bits, call RotateLeft32(x, -k). 30 | func RotateLeft32(x uint32, k int) uint32 { 31 | n := 32 32 | s := uint(k) & uint(n-1) 33 | return x<>(uint(n)-s) 34 | } 35 | 36 | // RotateLeft64 rotates x left by (k mod 64) bits. To rotate right by 37 | // k bits, call RotateLeft64(x, -k). 38 | func RotateLeft64(x uint64, k int) uint64 { 39 | n := 64 40 | s := uint(k) & uint(n-1) 41 | return x<>(uint(n)-s) 42 | } 43 | -------------------------------------------------------------------------------- /pkg/math/const.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2020-2023 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | package math 9 | 10 | const ( 11 | // MaxUint8 is the maximum unsigned 8-bit integer value. 12 | MaxUint8 = 0xff 13 | // MaxUint16 is the maximum unsigned 16-bit integer value. 14 | MaxUint16 = 0xffff 15 | // MaxUint32 is the maximum unsigned 32-bit integer value. 16 | MaxUint32 = 0xffffffff 17 | // MaxUint64 is the maximum unsigned 64-bit integer value. 18 | MaxUint64 = 0xffffffffffffffff 19 | 20 | // MaxInt32 is the maximum signed 8-bit integer value. 21 | MaxInt8 = 127 22 | // MaxInt32 is the maximum signed 16-bit integer value. 23 | MaxInt16 = 32767 24 | // MaxInt32 is the maximum signed 32-bit integer value. 25 | MaxInt32 = 2147483647 26 | // MaxInt32 is the maximum signed 64-bit integer value. 27 | MaxInt64 = 9223372036854775807 28 | ) 29 | -------------------------------------------------------------------------------- /pkg/math/doc.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2021 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | // Package math implements various mathematical algorithms and 9 | // provides commonly used constant values. 10 | package math 11 | -------------------------------------------------------------------------------- /pkg/math/integer.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2020-2021 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | package math 9 | 10 | // AddUint64 adds two unsigned 64-bit integer numbers. 11 | func AddUint64(a, b uint64) uint64 { 12 | return native("add64.circ", a, b) 13 | } 14 | 15 | // SubUint64 subtracts two unsigned 64-bit integer numbers. 16 | func SubUint64(a, b uint64) uint64 { 17 | return native("sub64.circ", a, b) 18 | } 19 | 20 | // MulUint64 multiplies two unsigned 64-bit integer numbers. 21 | func MulUint64(a, b uint64) uint64 { 22 | return native("mul64.circ", a, b) 23 | } 24 | 25 | // DivUint64 divides two unsigned 64-bit integer numbers. 26 | func DivUint64(a, b uint64) uint64 { 27 | return native("div64.circ", a, b) 28 | } 29 | 30 | // MaxUint returns the maximum of the argument unsigned integer numbers. 31 | func MaxUint(a, b uint) uint { 32 | if a > b { 33 | return a 34 | } 35 | return b 36 | } 37 | 38 | // MinUint returns the minimum of the argument unsigned integer numbers. 39 | func MinUint(a, b uint) uint { 40 | if a < b { 41 | return a 42 | } 43 | return b 44 | } 45 | -------------------------------------------------------------------------------- /pkg/math/modp.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2020 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | package math 9 | 10 | // Exp computes modular exponentiation b**e mod |m| (i.e. the sign of 11 | // m is ignore). 12 | func Exp(b, e, m uint) uint { 13 | rType := make(uint, size(m)) 14 | mType := make(uint, size(m)*2) 15 | 16 | var r mType = 1 17 | 18 | for i := size(e) - 1; i >= 0; i = i - 1 { 19 | r = r * r % mType(m) 20 | if e>>i&1 != 0 { 21 | r = r * mType(b) % mType(m) 22 | } 23 | } 24 | 25 | return rType(r) 26 | } 27 | -------------------------------------------------------------------------------- /pkg/math/montgomery.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2020-2021 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | package math 9 | 10 | func makeMontgomery(m uint) uint { 11 | n := size(m) 12 | tmpType := make(uint, size(m)*2+1) 13 | rrmType := make(uint, size(m)) 14 | 15 | var tmp tmpType = 1 16 | tmp = (tmp << (n * 2)) % tmpType(m) 17 | return rrmType(tmp) 18 | } 19 | 20 | func reduce(m, rrm, t uint) uint { 21 | n := size(m) 22 | a := t 23 | aType := make(uint, size(a)) 24 | 25 | for i := 0; i < n; i++ { 26 | if a&1 != 0 { 27 | a += aType(m) 28 | } 29 | a = a >> 1 30 | } 31 | if a >= aType(m) { 32 | a = a - aType(m) 33 | } 34 | return a 35 | } 36 | 37 | // ExpMontgomery computes modular exponentiation b**e mod |m| 38 | // (i.e. the sign of m is ignored). The m must be bigger than 0 and 39 | // not even number. 40 | func ExpMontgomery(b, e, m uint) uint { 41 | cType := make(uint, size(m)*2) 42 | 43 | rrm := makeMontgomery(m) 44 | 45 | t1 := cType(b) * cType(rrm) 46 | t2 := cType(e) * cType(rrm) 47 | 48 | r1 := math.reduce(m, rrm, t1) 49 | r2 := math.reduce(m, rrm, t2) 50 | 51 | prod := math.reduce(m, rrm, rrm) 52 | base := math.reduce(m, rrm, b*rrm) 53 | 54 | for i := 0; i < size(e); i++ { 55 | if e>>i&1 != 0 { 56 | prod = math.reduce(m, rrm, prod*base) 57 | } 58 | base = math.reduce(m, rrm, base*base) 59 | } 60 | 61 | return math.reduce(m, rrm, prod) 62 | } 63 | -------------------------------------------------------------------------------- /pkg/sort/sort.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | // Copyright (c) 2021 Markku Rossi 4 | // 5 | // All rights reserved. 6 | // 7 | 8 | // Package sort implements array sorting functions. 9 | package sort 10 | 11 | // Reverse reverses the argument slice. 12 | func Reverse(arr []int) []int { 13 | tmp := arr[0] 14 | for i := 0; i < len(arr)/2; i++ { 15 | tmp = arr[i] 16 | arr[i] = arr[len(arr)-1-i] 17 | arr[len(arr)-1-i] = tmp 18 | } 19 | return arr 20 | } 21 | 22 | // Sort sorts the argument slice in ascending order. 23 | func Slice(arr []int) []int { 24 | return bitonicSort(arr, 0, len(arr), true) 25 | } 26 | 27 | func bitonicSort(a []int, lo, n int, dir bool) []int { 28 | if n > 1 { 29 | m := n / 2 30 | a = bitonicSort(a, lo, m, !dir) 31 | a = bitonicSort(a, lo+m, n-m, dir) 32 | a = bitonicMerge(a, lo, n, dir) 33 | } 34 | return a 35 | } 36 | 37 | func bitonicMerge(a []int, lo, n int, dir bool) []int { 38 | if n > 1 { 39 | m := floorPow2(n - 1) 40 | tmp := a[0] 41 | for i := lo; i < lo+n-m; i++ { 42 | if dir == (a[i] > a[i+m]) { 43 | tmp = a[i] 44 | a[i] = a[i+m] 45 | a[i+m] = tmp 46 | } 47 | } 48 | a = bitonicMerge(a, lo, m, dir) 49 | a = bitonicMerge(a, lo+m, n-m, dir) 50 | } 51 | return a 52 | } 53 | -------------------------------------------------------------------------------- /rsa32.csv: -------------------------------------------------------------------------------- 1 | Version,Time,Total Gates,Non-XOR Gates 2 | 0,14.238416532,7366376,4220265 3 | 1,10.45587699,7366376,2155565 4 | 2,8.78036308,6822632,1935789 5 | 3,8.428656584,6769820,1874720 6 | 4,7.919823862,6717340,1821471 7 | 5,6.452876989,6717340,1821471 8 | 6,5.714601305,6717340,1821471 9 | 7,3.189555391,6717340,1821471 10 | 8,2.317192831,5972956,1603743 11 | 9,1.815048827,5972956,1603743 12 | 10,1.291853902,5539148,1493909 13 | 11,0.787331229,5539117,1493912 14 | -------------------------------------------------------------------------------- /rsa32.iql: -------------------------------------------------------------------------------- 1 | /* -*- sql -*- */ 2 | 3 | -- Internet Querly Language (IQL) file for processing 32bit RSA 4 | -- performance numbers (https://github.com/markkurossi/iql). 5 | 6 | -- Print percentages and values with two digits. 7 | SET REALFMT = '%.2f'; 8 | 9 | SELECT Version AS V, 10 | Time, 11 | HBAR(Time, 0, MAX(Time), 6) AS "[Time]", 12 | "Total Gates" AS Gates, 13 | "Non-XOR Gates" AS NonXOR, 14 | "Total Gates" / Time AS "Gates/s", 15 | "Non-XOR Gates" / Time AS "NonXOR/s", 16 | HBAR("Non-XOR Gates" / Time, 0, 17 | MAX("Non-XOR Gates" / Time), 8) AS "[!XOR/s]" 18 | FROM 'rsa32.csv'; 19 | -------------------------------------------------------------------------------- /testsuite/crypto/cipher/cts/aes128_cts_dec.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/cipher/cts" 7 | ) 8 | 9 | // @Hex 10 | // @LSB 11 | // @Test 0xc6353568f2bf8cb4d8a580362da7ff7f97 _ = 0x4920776f756c64206c696b652074686520 12 | func main(data []byte, e []byte) []byte { 13 | key := []byte{ 14 | 0x63, 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x20, 15 | 0x74, 0x65, 0x72, 0x69, 0x79, 0x61, 0x6b, 0x69, 16 | } 17 | var iv [16]byte 18 | return cts.DecryptAES128(key, iv, data) 19 | } 20 | -------------------------------------------------------------------------------- /testsuite/crypto/cipher/cts/aes128_cts_enc.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/cipher/cts" 7 | ) 8 | 9 | // @Hex 10 | // @LSB 11 | // @Test 0x4920776f756c64206c696b652074686520 _ = 0xc6353568f2bf8cb4d8a580362da7ff7f97 12 | func main(data []byte, e []byte) []byte { 13 | key := []byte{ 14 | 0x63, 0x68, 0x69, 0x63, 0x6b, 0x65, 0x6e, 0x20, 15 | 0x74, 0x65, 0x72, 0x69, 0x79, 0x61, 0x6b, 0x69, 16 | } 17 | var iv [16]byte 18 | return cts.EncryptAES128(key, iv, data) 19 | } 20 | -------------------------------------------------------------------------------- /testsuite/crypto/cipher/gcm/aes128_gcm.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/cipher/gcm" 7 | ) 8 | 9 | type Garbler struct { 10 | key []byte 11 | iv []byte 12 | plain []byte 13 | } 14 | 15 | // @Hex 16 | // @LSB 17 | // @Test 0x00000000000000000000000000000000,0x000000000000000000000000,_ _ = _ 0x58e2fccefa7e3061367f1d57a4e7455a 18 | // @Test 0x00000000000000000000000000000000,0x000000000000000000000000,0x00000000000000000000000000000000 _ = 0x0388dace60b6a392f328c2b971b2fe78 0xab6e47d42cec13bdf53a67b21257bddf 19 | // @Test 0xfeffe9928665731c6d6a8f9467308308,0xcafebabefacedbaddecaf888,0xd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255 _ = 0x42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985 0x4d5c2af327cd64a62cf35abd2ba6fab4 20 | // @Test 0xfeffe9928665731c6d6a8f9467308308,0xcafebabefacedbaddecaf888,0xd9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39 0xfeedfacedeadbeeffeedfacedeadbeefabaddad2 = 0x42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091 0x5bc94fbc3221a5db94fae95ae7121a47 21 | func main(g Garbler, aad []byte) ([]byte, []byte) { 22 | cipher := gcm.EncryptAES128(g.key, g.iv, g.plain, aad) 23 | split := len(g.plain) 24 | return cipher[:split], cipher[split:] 25 | } 26 | -------------------------------------------------------------------------------- /testsuite/crypto/hmac_sha1.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/hmac" 7 | ) 8 | 9 | // Test vectors from RFC-2202. 10 | 11 | // @Hex 12 | // @pprof 13 | // @LSB 14 | // @Test 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b 0x4869205468657265 = 0xb617318655057264e28bc0b6fb378c8ef146be00 15 | // @Test 0x4a656665 0x7768617420646f2079612077616e7420666f72206e6f7468696e673f = 0xeffcdf6ae5eb2fa2d27416d5f184df9c259a7c79 16 | // @Test 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd = 0x125d7342b9ac11cd91a39af48aa17b4f63f175d3 17 | // @Test 0x0102030405060708090a0b0c0d0e0f10111213141516171819 0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd = 0x4c9007f4026250c6bc8414f9bf50c86c2d7235da 18 | func main(key, data []byte) []byte { 19 | return hmac.SumSHA1(data, key) 20 | } 21 | -------------------------------------------------------------------------------- /testsuite/crypto/hmac_sha256.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/hmac" 7 | ) 8 | 9 | // Test vectors from RFC-4231. 10 | 11 | // @Hex 12 | // @pprof 13 | // @LSB 14 | // @Test 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b 0x4869205468657265 = 0xb0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7 15 | // @Test 0x4a656665 0x7768617420646f2079612077616e7420666f72206e6f7468696e673f = 0x5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843 16 | // @Test 0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0xdddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd = 0x773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe 17 | // @Test 0x0102030405060708090a0b0c0d0e0f10111213141516171819 0xcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd = 0x82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b 18 | func main(key, data []byte) []byte { 19 | return hmac.SumSHA256(data, key) 20 | } 21 | -------------------------------------------------------------------------------- /testsuite/crypto/rsa.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | package main 5 | 6 | import ( 7 | "crypto/rsa" 8 | ) 9 | 10 | // d: 0x321af139 11 | // n: 0xd60b2b09 12 | // e: 0x10001 13 | // 14 | // private: d, e 15 | // public: n, e 16 | 17 | // msg: 0x6d7472 18 | // cipher: 0x61f9ef88 19 | 20 | type Size = uint32 21 | 22 | type Garbler struct { 23 | msg Size 24 | privShare Size 25 | pubN Size 26 | pubE Size 27 | } 28 | 29 | // @Test 0x6d7472 0x321af130 0xd60b2b09 0x10001 9 = 0x55a83b79 30 | func main(g Garbler, privShare Size) uint { 31 | 32 | priv := g.privShare + privShare 33 | 34 | return rsa.Decrypt(g.msg, priv, g.pubN) 35 | } 36 | -------------------------------------------------------------------------------- /testsuite/crypto/sha1.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/sha1" 7 | ) 8 | 9 | // @Hex 10 | // @LSB 11 | // @Test 0x54686973207061676520696e74656e74696f6e616c6c79206c65667420626c616e6b2e _ = 0xaf064923bbf2301596aac4c273ba32178ebc4a96 12 | // @Test 0x2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2d2b2121 _ = 0x439cbaa98eefc429e16047dc4b1767671189f426 13 | func main(data, e []byte) []byte { 14 | return sha1.Sum(data) 15 | } 16 | -------------------------------------------------------------------------------- /testsuite/crypto/sha256_block.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/sha256" 7 | ) 8 | 9 | // @Hex 10 | // @Test 0 0 = 0x1fd35a2a9e6f530dd6e5e87075c4d98ebb2960e2b65085cddeae2f65853a3af 11 | func main(g, e byte) []byte { 12 | var data [50]byte 13 | data[0] = 0x6d 14 | data[1] = 0x74 15 | data[2] = 0x72 16 | return sha256.Sum256(data[:]) 17 | } 18 | -------------------------------------------------------------------------------- /testsuite/crypto/sha256_block_block.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/sha256" 7 | ) 8 | 9 | // @Hex 10 | // @Test 0 0 = 0xd650f570ed931e5cfe52bf339dd77493796f15e5c856a801efc90983e0bfa08b 11 | func main(g, e byte) []byte { 12 | var data [65]byte 13 | data[0] = 0x6d 14 | data[1] = 0x74 15 | data[2] = 0x72 16 | return sha256.Sum256(data[:]) 17 | } 18 | -------------------------------------------------------------------------------- /testsuite/crypto/sha256_block_pad.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/sha256" 7 | ) 8 | 9 | // @Hex 10 | // @Test 0 0 = 0x130d73e5967c29157813960ae640447ffd228b8cb5dba6ba641ebeb3cef2e914 11 | func main(g, e byte) []byte { 12 | var data [60]byte 13 | data[0] = 0x6d 14 | data[1] = 0x74 15 | data[2] = 0x72 16 | return sha256.Sum256(data[:]) 17 | } 18 | -------------------------------------------------------------------------------- /testsuite/crypto/sha512_block.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/sha512" 7 | ) 8 | 9 | // @Hex 10 | // @Test 0 0 = 0x257ebf75d3818e9de4f8b56f0272e452e8c9d6353ae9fbe284f950b3f79fc8027d171e0cb6e0029de0655a9ff90800db6574e25da5071a541f48a78b79514927 11 | func main(g, e byte) []byte { 12 | var data [110]byte 13 | data[0] = 0x6d 14 | data[1] = 0x74 15 | data[2] = 0x72 16 | return sha512.Sum512(data[:]) 17 | } 18 | -------------------------------------------------------------------------------- /testsuite/crypto/sha512_block_block.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/sha512" 7 | ) 8 | 9 | // @Hex 10 | // @Test 0 0 = 0xeb627442f972d3df69591b22d8a26eadb115c1c82a3e00767c45e3776906f2fb70e487f9282a5193777579954b84e881dbaa1832960e66039678b771b81a704e 11 | func main(g, e byte) []byte { 12 | var data [129]byte 13 | data[0] = 0x6d 14 | data[1] = 0x74 15 | data[2] = 0x72 16 | return sha512.Sum512(data[:]) 17 | } 18 | -------------------------------------------------------------------------------- /testsuite/crypto/sha512_block_pad.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/sha512" 7 | ) 8 | 9 | // @Hex 10 | // @Test 0 0 = 0xb380cd63710ae12c7f743585889f243e02bb673124ac56b7548f636e57d2fb42aeff4a6d7acbb8f223b008e309e4756a14d70b7842df7ff8f442ca9504e3ff2c 11 | func main(g, e byte) []byte { 12 | var data [124]byte 13 | data[0] = 0x6d 14 | data[1] = 0x74 15 | data[2] = 0x72 16 | return sha512.Sum512(data[:]) 17 | } 18 | -------------------------------------------------------------------------------- /testsuite/crypto/sha512_test_.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/sha512" 7 | ) 8 | 9 | // @Hex 10 | // @LSB 11 | // @Test 0 0 = 0xcf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e 12 | func main(g, e byte) []byte { 13 | var data [0]byte 14 | return sha512.Sum512(data[:]) 15 | } 16 | -------------------------------------------------------------------------------- /testsuite/crypto/sha512_test_abc.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "crypto/sha512" 7 | ) 8 | 9 | // @Hex 10 | // @LSB 11 | // @Test 0 0 = 0xddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f 12 | func main(g, e byte) []byte { 13 | var data [3]byte 14 | data[0] = 0x61 15 | data[1] = 0x62 16 | data[2] = 0x63 17 | return sha512.Sum512(data[:]) 18 | } 19 | -------------------------------------------------------------------------------- /testsuite/lang/array.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | type Field [10]int32 6 | 7 | // @Test 0 0 = 45 8 | // @Test 0 1 = 46 9 | func main(a, b int32) int { 10 | var arr Field 11 | 12 | for i := 0; i < len(arr); i++ { 13 | arr[i] = i 14 | } 15 | 16 | var sum int32 17 | for i := 0; i < len(arr); i++ { 18 | sum += arr[i] 19 | } 20 | 21 | return sum + b 22 | } 23 | -------------------------------------------------------------------------------- /testsuite/lang/assign2.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 1 7 = 1 7 6 | // @Test 7 1 = 1 7 7 | func main(a, b int32) (int, int) { 8 | min, max := minMax(a, b) 9 | return min, max 10 | } 11 | 12 | func minMax(a, b int) (int, int) { 13 | if a < b { 14 | return a, b 15 | } else { 16 | return b, a 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /testsuite/lang/cast_bytearr_string.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 0x40414243 1 = 0x40414243 6 | func main(a []byte, b int) []byte { 7 | str := string(a) 8 | return []byte(str) 9 | } 10 | -------------------------------------------------------------------------------- /testsuite/lang/cast_string_bytearr.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 0x40414243 1 = 0x40414243 6 | func main(a []byte, b int) string { 7 | return string(a) 8 | } 9 | -------------------------------------------------------------------------------- /testsuite/lang/cast_string_int.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 64 1 = 0x40 6 | func main(a, b uint8) string { 7 | return string(a) 8 | } 9 | -------------------------------------------------------------------------------- /testsuite/lang/composite_lit.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 0 0 = 21 6 | // @Test 1 2 = 24 7 | func main(a, b int32) int { 8 | arr := [3][2]int32{ 9 | {1, 2}, 10 | {3, 4}, 11 | {5, 6}, 12 | } 13 | 14 | var sum int32 15 | for i := 0; i < len(arr); i++ { 16 | sum += arr[i][0] 17 | sum += arr[i][1] 18 | } 19 | 20 | return sum + a + b 21 | } 22 | -------------------------------------------------------------------------------- /testsuite/lang/const_cast_bytearr_string.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // 0x48656c6c6f2c20776f726c6421 6 | 7 | // @Test 42 1 = 0x21646c726f77202c6f6c6c6548 8 | func main(a []byte, b int) []byte { 9 | ret := []byte("Hello, world!") 10 | return ret 11 | } 12 | -------------------------------------------------------------------------------- /testsuite/lang/const_cast_int_0.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 42 1 = -1 6 | func main(a, b uint64) int { 7 | val := int32(0x7fffffff) 8 | return int16(val) 9 | } 10 | -------------------------------------------------------------------------------- /testsuite/lang/const_cast_int_1.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // Test 42 1 = 65535 6 | func main(a, b uint64) uint { 7 | val := uint32(0xffffffff) 8 | return uint16(val) 9 | } 10 | -------------------------------------------------------------------------------- /testsuite/lang/const_cast_string_bytearr.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 42 1 = 0x21646c726f77202c6f6c6c6548 6 | func main(a []byte, b int) string { 7 | val := []byte{ 8 | 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x2c, 0x20, 0x77, 9 | 0x6f, 0x72, 0x6c, 0x64, 0x21, 10 | } 11 | ret := string(val) 12 | return ret 13 | } 14 | -------------------------------------------------------------------------------- /testsuite/lang/const_int.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 42 1 = 123 6 | func main(a, b int32) int { 7 | return 1 + 0x01 + 0b10 + 077 + 0o70 8 | } 9 | -------------------------------------------------------------------------------- /testsuite/lang/const_mod.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 42 1 = 1 6 | func main(a, b int32) int2 { 7 | if 1%2 == 0 { 8 | return 0 9 | } 10 | return 1 11 | } 12 | -------------------------------------------------------------------------------- /testsuite/lang/const_slice_arr_0.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Hex 6 | // @LSB 7 | // @Test 0 0 = 0x2c20776f726c6421 8 | func main(a, b int32) []byte { 9 | // 48656c6c6f 2c20776f726c6421 10 | data := []byte("Hello, world!") 11 | // data := []byte{72, 101, 108, 108, 111, 111} 12 | return foo(data[5:]) 13 | } 14 | 15 | func foo(data []byte) []byte { 16 | return data 17 | } 18 | -------------------------------------------------------------------------------- /testsuite/lang/const_slice_arr_1.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Hex 6 | // @LSB 7 | // @Test 0 0 = 0x2c20776f726c6421 8 | func main(a, b int32) []byte { 9 | // 48 65 6c 6c 6f | 2c 20 77 6f 72 6c 64 21 10 | data := []byte{ 11 | 0x48, 0x65, 0x6c, 0x6c, 0x6f, 12 | 0x2c, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64, 0x21, 13 | } 14 | return foo(data[5:]) 15 | } 16 | 17 | func foo(data []byte) []byte { 18 | return data 19 | } 20 | -------------------------------------------------------------------------------- /testsuite/lang/copy_make.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Hex 6 | // @LSB 7 | // @Test 0x11223344 0xaabb = 0x11223344aa 0x11223344aa 5 8 | func main(g, e []byte) ([]byte, []byte, int) { 9 | buf := make([]byte, 5) 10 | n := copy(buf, g) 11 | m := copy(buf[n:], e) 12 | 13 | buf2 := make([]byte, 5) 14 | copy(buf2, buf) 15 | 16 | return buf, buf2, n + m 17 | } 18 | -------------------------------------------------------------------------------- /testsuite/lang/copy_ptr.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | type FieldElement [2]int32 6 | 7 | var zero FieldElement 8 | 9 | func FeZero(fe *FieldElement) { 10 | copy(fe[:], zero[:]) 11 | } 12 | 13 | func FeOne(fe *FieldElement) { 14 | FeZero(fe) 15 | fe[0] = 1 16 | } 17 | 18 | func FeCopy(dst, src *FieldElement) { 19 | copy(dst[:], src[:]) 20 | } 21 | 22 | type ProjectiveGroupElement struct { 23 | X, Y, Z FieldElement 24 | } 25 | 26 | func (p *ProjectiveGroupElement) Zero() { 27 | FeZero(&p.X) 28 | FeOne(&p.Y) 29 | FeOne(&p.Z) 30 | } 31 | 32 | // @Test 0 0 = 2 33 | func main(a, b int32) int { 34 | var pge ProjectiveGroupElement 35 | 36 | pge.Zero() 37 | 38 | var y FieldElement 39 | FeCopy(&y, &pge.Y) 40 | 41 | return pge.X[0] + y[0] + pge.Z[0] 42 | } 43 | -------------------------------------------------------------------------------- /testsuite/lang/copy_slice_eq.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 0 0 = 45 6 | // @Test 1 2 = 48 7 | func main(a, b int32) int { 8 | var src [10]int32 9 | for i := 0; i < len(src); i++ { 10 | src[i] = i 11 | } 12 | 13 | var dst [10]int32 14 | copy(dst, src) 15 | 16 | var sum int32 17 | for i := 0; i < len(dst); i++ { 18 | sum += dst[i] 19 | } 20 | 21 | return a + b + sum 22 | } 23 | -------------------------------------------------------------------------------- /testsuite/lang/copy_slice_gt.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 0 0 = 10 6 | // @Test 1 2 = 13 7 | func main(a, b int32) int { 8 | var src [5]int32 9 | for i := 0; i < len(src); i++ { 10 | src[i] = i 11 | } 12 | 13 | var dst [10]int32 14 | copy(dst, src) 15 | 16 | var sum int32 17 | for i := 0; i < len(dst); i++ { 18 | sum += dst[i] 19 | } 20 | 21 | return a + b + sum 22 | } 23 | -------------------------------------------------------------------------------- /testsuite/lang/copy_slice_lt.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 0 0 = 10 6 | // @Test 1 2 = 13 7 | func main(a, b int32) int { 8 | var src [10]int32 9 | for i := 0; i < len(src); i++ { 10 | src[i] = i 11 | } 12 | 13 | var dst [5]int32 14 | copy(dst, src) 15 | 16 | var sum int32 17 | for i := 0; i < len(dst); i++ { 18 | sum += dst[i] 19 | } 20 | 21 | return a + b + sum 22 | } 23 | -------------------------------------------------------------------------------- /testsuite/lang/divi.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 43 4 = 10 6 | // @Test -43 4 = -10 7 | // @Test 43 -4 = -10 8 | // @Test -43 -4 = 10 9 | func main(a, b int64) int64 { 10 | return a / b 11 | } 12 | -------------------------------------------------------------------------------- /testsuite/lang/divu.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 42 1 = 42 6 | // @Test 42 2 = 21 7 | // @Test 42 3 = 14 8 | // @Test 42 4 = 10 9 | // @Test 42 5 = 8 10 | // @Test 42 6 = 7 11 | // @Test 42 7 = 6 12 | // @Test 42 8 = 5 13 | // @Test 42 9 = 4 14 | // @Test 42 10 = 4 15 | // @Test 42 11 = 3 16 | // @Test 42 12 = 3 17 | // @Test 42 13 = 3 18 | // @Test 42 14 = 3 19 | // @Test 42 20 = 2 20 | // @Test 42 30 = 1 21 | // @Test 42 40 = 1 22 | // @Test 42 42 = 1 23 | // @Test 42 43 = 0 24 | func main(a, b uint64) uint64 { 25 | return a / b 26 | } 27 | -------------------------------------------------------------------------------- /testsuite/lang/for.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 0 0 = 10 6 | func main(a, b uint8) int32 { 7 | var sum int32 = 0 8 | 9 | for i := 0; i < 5; i = i + 1 { 10 | sum = sum + i 11 | } 12 | 13 | return sum 14 | } 15 | -------------------------------------------------------------------------------- /testsuite/lang/len_array.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | type Field [10]int32 6 | 7 | // @Test 0 0 = 10 8 | func main(a, b int32) int { 9 | var arr Field 10 | return len(arr) 11 | } 12 | -------------------------------------------------------------------------------- /testsuite/lang/len_array_sum.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | type Field [10]int32 6 | 7 | // @Test 0 0 = 45 8 | // @Test 1 10 = 56 9 | func main(a, b int32) int { 10 | var arr Field 11 | 12 | for i := 0; i < len(arr); i++ { 13 | arr[i] = i 14 | } 15 | 16 | var sum int32 17 | for i := 0; i < len(arr); i++ { 18 | sum += arr[i] 19 | } 20 | 21 | return sum + a + b 22 | } 23 | -------------------------------------------------------------------------------- /testsuite/lang/len_string.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 0 0 = 13 6 | func main(a, b int32) int { 7 | val := "Hello, world!" 8 | return len(val) 9 | } 10 | -------------------------------------------------------------------------------- /testsuite/lang/len_string_sum.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 0 0 = 1161 6 | func main(a, b int32) int { 7 | val := "Hello, world!" 8 | 9 | var sum int32 10 | for i := 0; i < len(val); i++ { 11 | sum += int32(val[i]) 12 | } 13 | 14 | return sum 15 | } 16 | -------------------------------------------------------------------------------- /testsuite/lang/lshift0.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | package main 5 | 6 | // @Test 32 0 = 32 7 | func main(a, b uint64) uint64 { 8 | return a << 0 9 | } 10 | -------------------------------------------------------------------------------- /testsuite/lang/lshift1.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | package main 5 | 6 | // @Test 32 0 = 64 7 | func main(a, b uint64) uint64 { 8 | return a << 1 9 | } 10 | -------------------------------------------------------------------------------- /testsuite/lang/lshift64.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | package main 5 | 6 | // @Test 32 0 = 0 7 | func main(a, b uint64) uint64 { 8 | return a << 64 9 | } 10 | -------------------------------------------------------------------------------- /testsuite/lang/make.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 1 1 = 1 6 | // @Test 5 10 = 50 7 | func main(a, b int32) int { 8 | return mult(a, b) 9 | } 10 | 11 | func mult(a, b int) int { 12 | mType := make(int, size(a)*2) 13 | 14 | return mType(a) * mType(b) 15 | } 16 | -------------------------------------------------------------------------------- /testsuite/lang/modi.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 42 1 = 0 6 | // @Test 42 4 = 2 7 | // @Test -42 4 = 2 8 | // @Test 42 -4 = 2 9 | // @Test -42 -4 = 2 10 | func main(a, b int64) int64 { 11 | return a % b 12 | } 13 | -------------------------------------------------------------------------------- /testsuite/lang/modu.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 42 1 = 0 6 | // @Test 42 40 = 2 7 | // @Test 42 41 = 1 8 | // @Test 42 42 = 0 9 | // @Test 42 100 = 42 10 | func main(a, b uint64) uint64 { 11 | return a % b 12 | } 13 | -------------------------------------------------------------------------------- /testsuite/lang/mult.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 42 1 = 42 6 | // @Test 42 2 = 84 7 | // @Test 65536 0 = 0 8 | // @Test 65536 1 = 65536 9 | // @Test 65536 65536 = 4294967296 10 | // @Hex 11 | // @Test 0xff 0xff = 0xfe01 12 | // @Test 0xffff 0xff = 0xfeff01 13 | // @Test 0xff00 0x10 = 0xff000 14 | // @Test 0xff00 0x100 = 0xff0000 15 | // @Test 0xff 0xff = 0xfe01 16 | func main(a, b uint64) uint { 17 | return a * b 18 | } 19 | -------------------------------------------------------------------------------- /testsuite/lang/named_return.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | func sum(a, b int32) (result int32) { 6 | result = a + b 7 | return 8 | } 9 | 10 | // @Test 1 2 = 3 11 | // @Test 55 66 = 121 12 | func main(a, b int32) int { 13 | return sum(a, b) 14 | } 15 | -------------------------------------------------------------------------------- /testsuite/lang/named_return2.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 1 2 = 3 1 2 6 | // @Test 55 66 = 121 55 66 7 | func main(a, b int32) (int, int, int) { 8 | return sum(a, b) 9 | } 10 | 11 | func sum(a, b int) (h0, h1, h2 int) { 12 | h0 = a + b 13 | h1 = a 14 | h2 = b 15 | return 16 | } 17 | -------------------------------------------------------------------------------- /testsuite/lang/named_return3.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 1 2 = 3 1 2 6 | // @Test 55 66 = 121 55 66 7 | func main(a, b int32) (int, int, int) { 8 | return sum(a, b) 9 | } 10 | 11 | func sum(a, b int32) (h0, h1, h2 int32) { 12 | h0 = a + b 13 | h1 = a 14 | h2 = b 15 | return 16 | } 17 | -------------------------------------------------------------------------------- /testsuite/lang/pkg.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "math" 7 | ) 8 | 9 | const MaxUint64 = 42 10 | 11 | // @Test 0 0 = 42 0xffffffffffffffff 12 | func main(a, b uint64) (uint64, uint64) { 13 | return MaxUint64, math.MaxUint64 14 | } 15 | -------------------------------------------------------------------------------- /testsuite/lang/ptr.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | func bar(val *int32) int { 6 | *val = int32(42) 7 | return 7 8 | } 9 | 10 | // @Test 0 0 = 49 11 | // @Test 1 2 = 52 12 | func main(a, b int32) int { 13 | var c int32 14 | 15 | sum := a + b 16 | 17 | sum += bar(&c) 18 | 19 | return sum + c 20 | } 21 | -------------------------------------------------------------------------------- /testsuite/lang/ptr_array.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | type FieldElement [10]int32 6 | 7 | var zero FieldElement 8 | 9 | func FeZero(fe *FieldElement) { 10 | copy(fe[:], zero[:]) 11 | } 12 | 13 | func FeOne(fe *FieldElement) { 14 | FeZero(fe) 15 | fe[0] = 1 16 | fe[1] = 0 17 | } 18 | 19 | // @Test 0 0 = 1 20 | // @Test 1 2 = 4 21 | func main(a, b int32) int { 22 | var v FieldElement 23 | 24 | FeOne(&v) 25 | 26 | return a + b + v[0] 27 | } 28 | -------------------------------------------------------------------------------- /testsuite/lang/ptr_array_get.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | func sum(arr []int32) int32 { 6 | var sum int32 7 | for i := 0; i < len(arr); i++ { 8 | sum += arr[i] 9 | } 10 | return sum 11 | } 12 | 13 | func trampoline(a *[10]int32) int { 14 | return sum(a) 15 | } 16 | 17 | // @Test 0 0 = 0 18 | // @Test 1 0 = 5 19 | // @Test 1 2 = 15 20 | func main(a, b int32) int { 21 | var arr [10]int32 22 | 23 | for i := 0; i < len(arr); i++ { 24 | if i < len(arr)/2 { 25 | arr[i] = a 26 | } else { 27 | arr[i] = b 28 | } 29 | } 30 | return trampoline(&arr) 31 | } 32 | -------------------------------------------------------------------------------- /testsuite/lang/ptr_arrays.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | func Add(ptr *uint32, val uint32) { 6 | *ptr = val 7 | } 8 | 9 | // @Test 0 0 = 0 10 | // @Test 1 2 = 6 11 | // @Test 3 7 = 20 12 | func main(a, b uint32) uint { 13 | var sum [2][2]uint32 14 | 15 | Add(&sum[0][0], a) 16 | Add(&sum[0][1], a) 17 | Add(&sum[1][0], b) 18 | Add(&sum[1][1], b) 19 | 20 | return sum[0][0] + sum[0][1] + sum[1][0] + sum[1][1] 21 | } 22 | -------------------------------------------------------------------------------- /testsuite/lang/ptr_scopes.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | var c int32 6 | 7 | func set(ptr *int32, val int32) { 8 | *ptr = val 9 | } 10 | 11 | func setGlobal(val int32) { 12 | set(&c, val) 13 | } 14 | 15 | func getGlobal() int32 { 16 | return c 17 | } 18 | 19 | // @Test 0 0 = 0 20 | // @Test 1 2 = 3 21 | // @Test 7 9 = 16 22 | func main(a, b int32) int { 23 | var c int32 24 | 25 | set(&c, a) 26 | setGlobal(b) 27 | 28 | return c + getGlobal() 29 | } 30 | -------------------------------------------------------------------------------- /testsuite/lang/ptr_struct_field.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | type Foo struct { 6 | A int32 7 | B int32 8 | } 9 | 10 | func Set(ptr *int32, val int32) { 11 | *ptr = val 12 | } 13 | 14 | var f Foo 15 | 16 | // @Test 0 0 = 0 17 | // @Test 1 2 = 3 18 | // @Test 7 4 = 11 19 | func main(a, b int32) int { 20 | Set(&f.A, a) 21 | Set(&f.B, b) 22 | 23 | return f.A + f.B 24 | } 25 | -------------------------------------------------------------------------------- /testsuite/lang/rshift0.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | package main 5 | 6 | // @Test 32 0 = 32 7 | func main(a, b uint64) uint64 { 8 | return a >> 0 9 | } 10 | -------------------------------------------------------------------------------- /testsuite/lang/rshift1.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | package main 5 | 6 | // @Test 32 0 = 16 7 | func main(a, b uint64) uint64 { 8 | return a >> 1 9 | } 10 | -------------------------------------------------------------------------------- /testsuite/lang/rshift64.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | // 3 | 4 | package main 5 | 6 | // @Test 32 0 = 0 7 | func main(a, b uint64) uint64 { 8 | return a >> 64 9 | } 10 | -------------------------------------------------------------------------------- /testsuite/lang/sub.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 42 1 = 41 6 | // @Test 42 40 = 2 7 | // @Test 42 41 = 1 8 | // @Test 42 42 = 0 9 | // @Test 1584886686 1584886686 = 0 10 | // @Test 1584886686 1584886680 = 6 11 | func main(a, b uint64) uint64 { 12 | return a - b 13 | } 14 | -------------------------------------------------------------------------------- /testsuite/lang/test_ge.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 42 11 = 1 6 | // @Test 42 42 = 1 7 | // @Test 42 43 = 0 8 | // @Test 42 0 = 1 9 | // @Test 0 0 = 1 10 | // @Test 0 42 = 0 11 | // @Test 42 0xffff = 0 12 | // @Test 0xffff 0xffff = 1 13 | // @Test 0xffff 42 = 1 14 | func main(a, b uint16) bool { 15 | return a >= b 16 | } 17 | -------------------------------------------------------------------------------- /testsuite/lang/test_gt.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 42 11 = 1 6 | // @Test 42 42 = 0 7 | // @Test 42 43 = 0 8 | // @Test 42 0 = 1 9 | // @Test 0 0 = 0 10 | // @Test 0 42 = 0 11 | // @Test 42 0xffff = 0 12 | // @Test 0xffff 0xffff = 0 13 | // @Test 0xffff 42 = 1 14 | func main(a, b uint16) bool { 15 | return a > b 16 | } 17 | -------------------------------------------------------------------------------- /testsuite/lang/test_le.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 42 11 = 0 6 | // @Test 42 42 = 1 7 | // @Test 42 43 = 1 8 | // @Test 42 0 = 0 9 | // @Test 0 0 = 1 10 | // @Test 0 42 = 1 11 | // @Test 42 0xffff = 1 12 | // @Test 0xffff 0xffff = 1 13 | // @Test 0xffff 42 = 0 14 | func main(a, b uint16) bool { 15 | return a <= b 16 | } 17 | -------------------------------------------------------------------------------- /testsuite/lang/test_lt.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 42 11 = 0 6 | // @Test 42 42 = 0 7 | // @Test 42 43 = 1 8 | // @Test 42 0 = 0 9 | // @Test 0 0 = 0 10 | // @Test 0 42 = 1 11 | // @Test 42 0xffff = 1 12 | // @Test 0xffff 0xffff = 0 13 | // @Test 0xffff 42 = 0 14 | func main(a, b uint16) bool { 15 | return a < b 16 | } 17 | -------------------------------------------------------------------------------- /testsuite/lang/var.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 0 0 = 0 6 | // @Test 1 2 = 3 7 | func main(a, b int32) int32 { 8 | var r int32 9 | 10 | r = a + b 11 | 12 | return r 13 | } 14 | -------------------------------------------------------------------------------- /testsuite/lang/var2.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 0 0 = 42 6 | // @Test 1 2 = 45 7 | func main(a, b int32) int32 { 8 | var r int32 = 42 9 | 10 | r = r + a + b 11 | 12 | return r 13 | } 14 | -------------------------------------------------------------------------------- /testsuite/lang/var3.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | var base uint32 = 42 6 | 7 | // @Test 0 0 = 42 8 | // @Test 1 2 = 45 9 | func main(a, b uint32) uint { 10 | return base + a + b 11 | } 12 | -------------------------------------------------------------------------------- /testsuite/lang/var4.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 0 0 = 0 0 0 42 6 | // @Test 1 2 = 3 1 2 42 7 | func main(a, b int32) (int, int, int, int) { 8 | var z int = int128(42) 9 | return a + b, a, b, z 10 | } 11 | -------------------------------------------------------------------------------- /testsuite/lang/zerolabel.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | // @Test 0 0 = 2 6 | func main(a, b uint2) uint { 7 | var r uint2 = 1 // init 1 bugs, 0 works. 8 | 9 | r = r * 1 10 | r = r + 1 11 | 12 | return r 13 | } 14 | -------------------------------------------------------------------------------- /testsuite/math/bits/rotate_left_16.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "math/bits" 7 | ) 8 | 9 | // @Test 15 -2 = 49155 10 | func main(a, b uint16) uint16 { 11 | return bits.RotateLeft16(a, -2) 12 | } 13 | -------------------------------------------------------------------------------- /testsuite/math/bits/rotate_left_32.mpcl: -------------------------------------------------------------------------------- 1 | // -*- go -*- 2 | 3 | package main 4 | 5 | import ( 6 | "math/bits" 7 | ) 8 | 9 | // @Test 15 -2 = 3221225475 10 | func main(a, b uint32) uint32 { 11 | return bits.RotateLeft32(a, -2) 12 | } 13 | -------------------------------------------------------------------------------- /types/parse.go: -------------------------------------------------------------------------------- 1 | // 2 | // parse.go 3 | // 4 | // Copyright (c) 2021-2024 Markku Rossi 5 | // 6 | // All rights reserved. 7 | // 8 | 9 | package types 10 | 11 | import ( 12 | "fmt" 13 | "regexp" 14 | "strconv" 15 | ) 16 | 17 | var ( 18 | reArr = regexp.MustCompilePOSIX(`^\[([[:digit:]]*)\](.+)$`) 19 | reSized = regexp.MustCompilePOSIX(`^([[:alpha:]]+)([[:digit:]]*)$`) 20 | ) 21 | 22 | // Parse parses type definition and returns its type information. 23 | func Parse(val string) (info Info, err error) { 24 | var ival int64 25 | 26 | switch val { 27 | case "b", "bool": 28 | info = Bool 29 | return 30 | 31 | case "byte": 32 | info = Byte 33 | return 34 | 35 | case "rune": 36 | info = Rune 37 | return 38 | } 39 | 40 | m := reSized.FindStringSubmatch(val) 41 | if m != nil { 42 | switch m[1] { 43 | case "b", "bool": 44 | info.Type = TBool 45 | 46 | case "i", "int": 47 | info.Type = TInt 48 | 49 | case "u", "uint": 50 | info.Type = TUint 51 | 52 | case "s", "string": 53 | info.Type = TString 54 | 55 | case "struct": 56 | info.Type = TStruct 57 | 58 | default: 59 | return info, fmt.Errorf("types.Parse: unknown type: %s", val) 60 | } 61 | var bits int64 62 | if len(m[2]) > 0 { 63 | bits, err = strconv.ParseInt(m[2], 10, 32) 64 | if err != nil { 65 | return 66 | } 67 | info.IsConcrete = true 68 | } 69 | info.Bits = Size(bits) 70 | info.MinBits = info.Bits 71 | return 72 | } 73 | 74 | m = reArr.FindStringSubmatch(val) 75 | if m == nil { 76 | return info, fmt.Errorf("types.Parse: unknown type: %s", val) 77 | } 78 | var elType Info 79 | elType, err = Parse(m[2]) 80 | if err != nil { 81 | return 82 | } 83 | if len(m[1]) > 0 { 84 | ival, err = strconv.ParseInt(m[1], 10, 32) 85 | if err != nil { 86 | return 87 | } 88 | info.Type = TArray 89 | } else { 90 | info.Type = TSlice 91 | } 92 | 93 | info.IsConcrete = true 94 | info.Bits = Size(ival) * elType.Bits 95 | info.MinBits = info.Bits 96 | info.ElementType = &elType 97 | info.ArraySize = Size(ival) 98 | 99 | return 100 | } 101 | -------------------------------------------------------------------------------- /types/parse_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2021-2024 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package types 8 | 9 | import ( 10 | "testing" 11 | ) 12 | 13 | var parseTests = []struct { 14 | input string 15 | info Info 16 | }{ 17 | { 18 | input: "b", 19 | info: Bool, 20 | }, 21 | { 22 | input: "bool", 23 | info: Bool, 24 | }, 25 | { 26 | input: "byte", 27 | info: Byte, 28 | }, 29 | { 30 | input: "rune", 31 | info: Rune, 32 | }, 33 | { 34 | input: "i32", 35 | info: Int32, 36 | }, 37 | { 38 | input: "int32", 39 | info: Int32, 40 | }, 41 | { 42 | input: "u32", 43 | info: Uint32, 44 | }, 45 | { 46 | input: "uint32", 47 | info: Uint32, 48 | }, 49 | { 50 | input: "i32", 51 | info: Int32, 52 | }, 53 | { 54 | input: "int32", 55 | info: Int32, 56 | }, 57 | { 58 | input: "string8", 59 | info: Info{ 60 | Type: TString, 61 | IsConcrete: true, 62 | Bits: 8, 63 | MinBits: 8, 64 | }, 65 | }, 66 | { 67 | input: "[8]byte", 68 | info: Info{ 69 | Type: TArray, 70 | IsConcrete: true, 71 | Bits: 64, 72 | MinBits: 64, 73 | ElementType: &Info{ 74 | Type: TUint, 75 | IsConcrete: true, 76 | Bits: 8, 77 | MinBits: 8, 78 | }, 79 | ArraySize: 8, 80 | }, 81 | }, 82 | { 83 | input: "[]byte", 84 | info: Info{ 85 | Type: TSlice, 86 | IsConcrete: true, 87 | Bits: 0, 88 | MinBits: 0, 89 | ElementType: &Info{ 90 | Type: TUint, 91 | IsConcrete: true, 92 | Bits: 8, 93 | MinBits: 8, 94 | }, 95 | ArraySize: 0, 96 | }, 97 | }, 98 | } 99 | 100 | func TestParse(t *testing.T) { 101 | for idx, test := range parseTests { 102 | info, err := Parse(test.input) 103 | if err != nil { 104 | t.Errorf("parseTest[%d]: %s\n", idx, err) 105 | continue 106 | } 107 | if !info.Equal(test.info) { 108 | t.Errorf("%v != %v", info, test.info) 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /types/types_test.go: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) 2020-2024 Markku Rossi 3 | // 4 | // All rights reserved. 5 | // 6 | 7 | package types 8 | 9 | import ( 10 | "testing" 11 | ) 12 | 13 | func TestUndefined(t *testing.T) { 14 | undef := Info{} 15 | if !undef.Undefined() { 16 | t.Errorf("undef is not undefined") 17 | } 18 | } 19 | 20 | func TestInstantiate(t *testing.T) { 21 | testInstantiate(t, Info{ 22 | Type: TInt, 23 | }, Int32) 24 | testInstantiate(t, Info{ 25 | Type: TUint, 26 | }, Uint32) 27 | testInstantiate(t, Info{ 28 | Type: TUint, 29 | }, Uint64) 30 | 31 | // Array instantiation. 32 | 33 | at10 := Info{ 34 | Type: TArray, 35 | IsConcrete: true, 36 | Bits: 10 * 8, 37 | MinBits: 10 * 8, 38 | ElementType: &Byte, 39 | ArraySize: 10, 40 | } 41 | testInstantiate(t, Info{ 42 | Type: TArray, 43 | ElementType: &Byte, 44 | }, at10) 45 | 46 | at10Ptr := Info{ 47 | Type: TPtr, 48 | IsConcrete: true, 49 | ElementType: &at10, 50 | } 51 | testInstantiate(t, Info{ 52 | Type: TArray, 53 | ElementType: &Byte, 54 | }, at10Ptr) 55 | 56 | // Slice instantiation. 57 | st := Info{ 58 | Type: TSlice, 59 | ElementType: &Byte, 60 | } 61 | testInstantiate(t, st, at10) 62 | testInstantiate(t, st, at10Ptr) 63 | } 64 | 65 | func testInstantiate(t *testing.T, i, o Info) { 66 | if !i.Instantiate(o) { 67 | t.Errorf("%v.Instantiate(%v) failed", i, o) 68 | } 69 | } 70 | --------------------------------------------------------------------------------