Commit ab6f2bda authored by Anders Jensen Løvig's avatar Anders Jensen Løvig
Browse files

Merge branch 'dev-anders' into 'main'

Handin 6

See merge request !1
parents fbc0ec4b 1f647389
# Cryptographic Computing - Handin 5
# Cryptographic Computing - Handin 6
A Go implementation of a passive secure two-party protocol for blood type compatibility based on Yao's garbled circuit protocol.
A Go implementation of a passive secure two-party protocol for blood type compatibility based on a homomorphic encryption scheme.
## Implementation
The protocol is implemented in `cmd/handin5/main.go` with tests in `cmd/handin5/main_test.go`.
The protocol is implemented in `cmd/handin6/main.go` with tests in `cmd/handin6/main_test.go`.
For the implementation we have implemented support packages which can be reused:
- `internal/crypto/elgamal` implements textbook ElGamal encryption.
- `internal/crypto/oblivious` implements a 1 out of n oblivious transfer protocol.
- `internal/crypto/garbled` implements Yao's garbled circuits.
- `internal/crypto/garbled` implements Yao's garbled circuits.
- `internal/crypto/homomorphic` implements the homomorphic encryption scheme.
### Notes on implementation.
1. For the security parameters we choose 1000-bit p, 10^6-bit q, 40-bit r and 1000 elements in the public key. We choose those values
because they improve the performance of the scheme. These parameters guarantee that we decrypt the result after evaluating the blood compatibility function.
2. In the homomorphic encryption scheme, we need to choose a random subset S. We do this by shuffling a list containing the numbers [1,...,n]
and then choosing the k first numbers in the list, where k is a random integer in the range [1,...,n). This ensures we always add some noise to the encryption.
## Requirements
......@@ -19,11 +28,11 @@ The protocol is tested using [Go 1.17](https://golang.org/dl/), but will likely
You can run the tests with go:
```
go test ./cmd/handin5
go test ./cmd/handin6
```
This executes the protocol tests. This includes a test that tries the protocol on all combinations of recipient and donor blood types.
To run all tests in the repository (could take some time):
```
go test ./...
```
\ No newline at end of file
```
package main
import (
"crycomp/internal/blood"
"crycomp/internal/crypto/homomorphic"
"crycomp/internal/crypto/util"
"crypto/rand"
"fmt"
"math/big"
)
func main() {
bloodA, bloodB := blood.Type_ABn, blood.Type_ABn
z, err := RunProtocol(bloodA, bloodB)
if err != nil {
fmt.Println("Protocol failed with error:", err)
} else if z == blood.Table[bloodA][bloodB] {
fmt.Println("Protocol succeded")
} else {
fmt.Printf("Protocol failed, output was %t, but should be %t", z, blood.Table[bloodA][bloodB])
}
}
type Party struct {
conn chan *big.Int
}
func (alice *Party) RunAlice(x int, priv *homomorphic.PrivateKey) (result bool, err error) {
xval := make([]int, 3)
//Negate values
for i, val := range util.Int2Bools(x, 3) {
if !val {
xval[i] = 1
}
}
// Send encoded input ints to bob
for i := 0; i < 3; i++ {
c, err := homomorphic.Encrypt(rand.Reader, xval[i], &priv.PublicKey)
if err != nil {
return false, err
}
alice.conn <- c
}
//Receive and decode result from bob
result = homomorphic.Decrypt(<-alice.conn, priv) == 1 //Convert 1 -> true
// result = alice.HEProt.Decode(<-alice.conn, *alice.sKey) == 1 //Convert 1 -> true
return
}
func (bob *Party) RunBob(y int, pub *homomorphic.PublicKey) (err error) {
c_y := make([]*big.Int, 3)
for i, val := range util.Int2Bools(y, 3) {
bit := 0
if val {
bit = 1
}
c_y[i], err = homomorphic.Encrypt(rand.Reader, bit, pub)
if err != nil {
return
}
// c_y[i] = bob.HEProt.Encode(bit)
}
//Receive ciphertexts from alice
c_x := make([]*big.Int, 3)
for i := range c_x {
c_x[i] = <-bob.conn
}
c, err := evaluate(c_x, c_y, pub)
if err != nil {
return
}
bob.conn <- c
// bob.conn <- *bob.HEProt.Eval(c_x, c_y)
return
}
func evaluate(x, y []*big.Int, pub *homomorphic.PublicKey) (result *big.Int, err error) {
one, err := homomorphic.Encrypt(rand.Reader, 1, pub)
if err != nil {
return
}
tmp := make([]*big.Int, 3)
for i := 0; i < 3; i++ {
v := new(big.Int).Mul(x[i], y[i])
tmp[i] = v.Add(v, one)
}
result = new(big.Int).Mul(tmp[0], tmp[1])
result.Mul(result, tmp[2])
return
}
// RunProtocol runs the protocol between receiving blood type x and donor blood
// type y.
func RunProtocol(x, y int) (z bool, err error) {
conn := make(chan *big.Int)
Alice := &Party{conn}
Bob := &Party{conn}
privKey, err := homomorphic.GenerateKey(rand.Reader, 1000, 1000000, 40, 1000)
if err != nil {
return
}
// Concurrently run Bob
go func() {
err := Bob.RunBob(y, &privKey.PublicKey)
if err != nil {
panic(err) // TODO do not panic
}
}()
z, err = Alice.RunAlice(x, privKey)
return
}
package main
import (
"crycomp/internal/blood"
"fmt"
"testing"
)
func TestProtocol(t *testing.T) {
// Runs the protocol for all combinations of recipient and donor blood types.
n := len(blood.Table)
for x := 0; x < n; x++ {
for y := 0; y < n; y++ {
testName := fmt.Sprintf("(x=%s,y=%s)", blood.Names[x], blood.Names[y])
t.Run(testName, func(t *testing.T) {
z, err := RunProtocol(x, y)
if err != nil {
t.Errorf("Protocol error: %s", err)
} else if z != blood.Table[x][y] {
t.Fatalf("Failed blood compatibility test for index [%d,%d]. Expected %t, got %t", x, y, !z, z)
}
})
}
}
}
package circuit
type Wire = int
type WireValue = bool
type Circuit struct {
wires []WireValue
}
// // The function that evaluates this gate.
// fun gateFun
// // The input wire indices
// inputs []int
// // The output wire index
// output int
// }
// type Builder struct {
// numInputs int
// gates []Gate
// }
// func NewBuilder(numInputs int) *Builder {
// return &Builder{
// numInputs: numInputs,
// gates: make([]*gate, 0),
// }
// }
// func (b *Builder) AddNotGate(in int) (out int) {
// b.AddGate()
// }
// func (b *Builder) allocWires(count int) (wires []int) {
// }
// func btoi(b bool) (i int) {
// if b {
// i = 1
// }
// return
// }
// func itob(i int) (b bool) {
// if i != 0 {
// b = true
// }
// return
// }
package circuit
import "testing"
func TestBuilderInputs(t *testing.T) {
// b := NewBuilder()
// A_in := b.AddInputs(3)
// if len(A_in) != 3 {
// t.Errorf("Expected length of A_in to be 3 (was %d)", len(A_in))
// }
// for i := 0; i < len(A_in); i++ {
// if A_in[i] != i {
// t.Errorf("Expected A_in[%d] == %d (was %d)", i, i, A_in[i])
// }
// if b.wires[A_in[i]] != i {
// t.Errorf("Expected b.wires[%d] == %d (was %d)", A_in[i], i, b.wires[A_in[i]])
// }
// }
// B_in := b.AddInputs(1)
// if len(B_in) != 1 {
// t.Errorf("Expected length of B_in to be 1 (was %d)", len(B_in))
// }
// for i := 0; i < len(B_in); i++ {
// if B_in[i] != i+3 {
// t.Errorf("Expected B_in[%d] == %d (was %d)", i, i+3, B_in[i])
// }
// }
}
package circuit
type Gate interface {
Evalaute(c *Circuit)
}
//////////////// NOT ////////////////
type NotGate struct {
in Wire
out Wire
}
func (g *NotGate) Evaluate(c *Circuit) {
c.wires[g.out] = !c.wires[g.in]
}
//////////////// AND ////////////////
type AndGate struct {
in []Wire
out Wire
}
func (g *AndGate) Evaluate(c *Circuit) {
r := true
for w := range g.in {
r = r && c.wires[w]
}
c.wires[g.out] = r
}
//////////////// OR /////////////////
type OrGate struct {
in []Wire
out Wire
}
func (g *OrGate) Evaluate(c *Circuit) {
r := false
for w := range g.in {
r = r || c.wires[w]
}
c.wires[g.out] = r
}
package circuit
import "testing"
func TestNotGate(t *testing.T) {
c := &Circuit{wires: make([]bool, 2)}
g := &NotGate{in: 0, out: 1}
g.Evaluate(c)
if c.wires[1] != true {
t.Errorf("Expected NOT gate: (0) -> (1)")
}
c.wires[0] = true
g.Evaluate(c)
if c.wires[1] != false {
t.Errorf("Expected NOT gate: (1) -> (0)")
}
}
func TestAndGate(t *testing.T) {
c := &Circuit{wires: make([]bool, 3)}
g := &AndGate{in: []Wire{0, 1}, out: 2}
g.Evaluate(c)
if c.wires[2] != false {
t.Errorf("Expected AND gate: (0, 0) -> (0)")
}
c.wires[0] = true
g.Evaluate(c)
if c.wires[2] != false {
t.Errorf("Expected AND gate: (1, 0) -> (0)")
}
c.wires[1] = true
g.Evaluate(c)
if c.wires[2] != true {
t.Errorf("Expected AND gate: (1, 1) -> (1)")
}
c.wires[0] = false
g.Evaluate(c)
if c.wires[2] != false {
t.Errorf("Expected AND gate: (0, 1) -> (0)")
}
c.wires = []bool{true, true, false, false}
g = &AndGate{in: []Wire{0, 1, 2}, out: 3}
g.Evaluate(c)
if c.wires[3] != false {
t.Errorf("Expected AND gate: (1, 1, 0) -> (0)")
}
c.wires[2] = true
g.Evaluate(c)
if c.wires[3] != true {
t.Errorf("Expected AND gate: (1, 1, 1) -> (1)")
}
}
func TestOrGate(t *testing.T) {
c := &Circuit{wires: make([]bool, 3)}
g := &OrGate{in: []Wire{0, 1}, out: 2}
g.Evaluate(c)
if c.wires[2] != false {
t.Errorf("Expected OR gate: (0, 0) -> (0)")
}
c.wires[0] = true
g.Evaluate(c)
if c.wires[2] != true {
t.Errorf("Expected OR gate: (1, 0) -> (1)")
}
c.wires[1] = true
g.Evaluate(c)
if c.wires[2] != true {
t.Errorf("Expected OR gate: (1, 1) -> (1)")
}
c.wires[0] = false
g.Evaluate(c)
if c.wires[2] != true {
t.Errorf("Expected OR gate: (0, 1) -> (1)")
}
c.wires = []bool{false, false, true, false}
g = &OrGate{in: []Wire{0, 1, 2}, out: 3}
g.Evaluate(c)
if c.wires[3] != true {
t.Errorf("Expected OR gate: (0, 0, 1) -> (1)")
}
c.wires[2] = false
g.Evaluate(c)
if c.wires[3] != false {
t.Errorf("Expected OR gate: (0, 0, 0) -> (0)")
}
}
......@@ -56,7 +56,7 @@ func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err
}
m = encodeMessage(pub, m)
r, err := util.RandInt(random, pub.Q)
r, err := util.RandIntn(random, pub.Q)
if err != nil {
return
}
......
package homomorphic
import (
"crycomp/internal/crypto/util"
"io"
"math/big"
)
var two = big.NewInt(2)
type Params struct {
pLen int
qLen int
rLen int
n int
}
func NewParams(pLen, qLen, rLen, m int) *Params {
return &Params{
pLen: pLen,
qLen: qLen,
rLen: rLen,
n: m,
}
}
type PublicKey struct {
y []*big.Int
n int
}
type PrivateKey struct {
PublicKey
p *big.Int
}
func GenerateKey(random io.Reader, pLen, qLen, rLen, n int) (priv *PrivateKey, err error) {
// Chose a random big secret odd integer p.
p, err := util.RandIntk(random, pLen)
if err != nil {
return
}
p.SetBit(p, 0, 1) // Make p odd.
y := make([]*big.Int, n)
var q *big.Int
var r *big.Int
for i := 0; i < n; i++ {
q, err = util.RandIntk(random, qLen)
if err != nil {
return
}
r, err = util.RandIntk(random, rLen)
if err != nil {
return
}
y[i] = q.Mul(p, q).Add(q, r.Mul(r, two))
}
priv = &PrivateKey{
PublicKey: PublicKey{
y: y,
n: n,
},
p: p,
}
return
}
func Encrypt(random io.Reader, m int, pub *PublicKey) (c *big.Int, err error) {
// Sample random subset S of [0...n]
S := make([]int, pub.n)
for i := 0; i < pub.n; i++ {
S[i] = i + 1
}
util.Shuffle(pub.n, func(i, j int) { S[i], S[j] = S[j], S[i] })
sLen, err := util.RandIntn(random, big.NewInt(int64(pub.n)))
if err != nil {
return
}
S = S[:sLen.Int64()]
c = big.NewInt(int64(m))
for i := range S {
c = c.Add(c, pub.y[S[i]-1])
}
return
}
func Decrypt(c *big.Int, priv *PrivateKey) int {
m := new(big.Int).Mod(c, priv.p)
m.Mod(m, two)
return int(m.Int64())
}
......@@ -21,7 +21,7 @@ type Encryption struct {
// using elgamal.GenerateKey. The way the key is generated the corresponding
// private key is unknown.
func oGenElgamel(random io.Reader, params *group.Params) (pub *elgamal.PublicKey, err error) {
s, err := util.RandInt(random, params.P)
s, err := util.RandIntn(random, params.P)
if err != nil {
return
}
......
......@@ -42,8 +42,8 @@ func XOR(a, b []byte) (dst []byte) {
var one = big.NewInt(1)
// RandInt returns a random integer in the range [1, n).
func RandInt(random io.Reader, n *big.Int) (r *big.Int, err error) {
// RandIntn returns a random integer in the range [1, n).
func RandIntn(random io.Reader, n *big.Int) (r *big.Int, err error) {
tmp := new(big.Int).Set(n)
tmp.Sub(tmp, one)
......@@ -56,12 +56,35 @@ func RandInt(random io.Reader, n *big.Int) (r *big.Int, err error) {
return
}
// RandIntk returns a random k-bit integer.
func RandIntk(random io.Reader, k int) (r *big.Int, err error) {
// Calculate how many bytes are needed to have k bits.
bLen := (k + 7) / 8
// Fill bytes with random data
bytes := make([]byte, bLen)
_, err = io.ReadFull(random, bytes)
if err != nil {
return
}
// Clear the bits that exceed k bits
bytes[0] &= 0xff >> (8*len(bytes) - k)
r = new(big.Int).SetBytes(bytes)
// Set the k-1 bit. This ensures a k-bits integer
// and the distribution is maintained (all other bits are random)
r.SetBit(r, k-1, 1)
return
}
type cryptoSource [8]byte
func Perm(n int) []int {
return mRand.New(&cryptoSource{}).Perm(n)
}
func Shuffle(n int, swap func(i, j int)) {
mRand.New(&cryptoSource{}).Shuffle(n, swap)
}
func (s *cryptoSource) Int63() int64 {
_, err := cRand.Read(s[:])
if err != nil {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment