├── .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 |
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 | crc a{1,0}u1 uint1 b{1,0}u1 uint1 c{1,0}u1 uint1 %ret0{1,1}u1 uint1
--------------------------------------------------------------------------------
/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 |
20 |
--------------------------------------------------------------------------------
/circuit/docs/not.svg:
--------------------------------------------------------------------------------
1 |
21 |
--------------------------------------------------------------------------------
/circuit/docs/or.svg:
--------------------------------------------------------------------------------
1 |
22 |
--------------------------------------------------------------------------------
/circuit/docs/wire.svg:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/circuit/docs/xnor.svg:
--------------------------------------------------------------------------------
1 |
27 |
--------------------------------------------------------------------------------
/circuit/docs/xor.svg:
--------------------------------------------------------------------------------
1 |
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 = `
44 | `
45 |
46 | var tmplXORExpanded = `
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 |
--------------------------------------------------------------------------------