From 65803542f652ee334037e0e6f7c3688121ddafa5 Mon Sep 17 00:00:00 2001 From: zhdllwyc Date: Sat, 3 Sep 2022 00:38:05 -0500 Subject: [PATCH] simOT --- ot/simot/simot_test.go | 231 +++++++++++++++++++++++++++++++++++++++ ot/simot/simotlocal.go | 238 +++++++++++++++++++++++++++++++++++++++++ ot/simot/simotparty.go | 29 +++++ 3 files changed, 498 insertions(+) create mode 100644 ot/simot/simot_test.go create mode 100644 ot/simot/simotlocal.go create mode 100644 ot/simot/simotparty.go diff --git a/ot/simot/simot_test.go b/ot/simot/simot_test.go new file mode 100644 index 000000000..b838ebac8 --- /dev/null +++ b/ot/simot/simot_test.go @@ -0,0 +1,231 @@ +// Reference: https://eprint.iacr.org/2015/267.pdf (1 out of 2 OT case) +// Sender has 2 messages m0, m1 +// Receiver receives mc based on the choice bit c + +package simot + +import ( + "bytes" + "crypto/rand" + "testing" + + "github.com/cloudflare/circl/group" +) + +const testSimOTCount = 100 + +func simOT(myGroup group.Group, sender *Sender, receiver *Receiver, m0, m1 []byte, choice, index int) error { + // Initialization + A := sender.InitSender(myGroup, m0, m1, index) + + // Round 1 + // Sender sends A to receiver + B := receiver.Round1Receiver(myGroup, choice, index, A) + + // Round 2 + // Receiver sends B to sender + e0, e1 := sender.Round2Sender(B) + + // Round 3 + // Sender sends e0 e1 to receiver + errDec := receiver.Round3Receiver(e0, e1, receiver.c) + if errDec != nil { + return errDec + } + + return nil +} + +func testNegativeSimOT(t *testing.T, myGroup group.Group, choice int) { + var sender Sender + var receiver Receiver + m0 := make([]byte, myGroup.Params().ScalarLength) + m1 := make([]byte, myGroup.Params().ScalarLength) + _, errRand := rand.Read(m0) + if errRand != nil { + panic(errRand) + } + _, errRand = rand.Read(m1) + if errRand != nil { + panic(errRand) + } + + // Initialization + A := sender.InitSender(myGroup, m0, m1, 0) + + // Round 1 + B := receiver.Round1Receiver(myGroup, choice, 0, A) + + // Round 2 + e0, e1 := sender.Round2Sender(B) + // Round 3 + + // Here we pass in the flipped choice bit, to prove the decryption will fail + // The receiver will not learn anything about m_{1-c} + errDec := receiver.Round3Receiver(e0, e1, 1-choice) + if errDec == nil { + t.Error("SimOT decryption failed", errDec) + } + + if choice == 0 { + equal0 := bytes.Compare(sender.m0, receiver.mc) + if equal0 == 0 { + t.Error("Receiver decryption should fail") + } + equal1 := bytes.Compare(sender.m1, receiver.mc) + if equal1 == 0 { + t.Error("Receiver decryption should fail") + } + } else { + equal0 := bytes.Compare(sender.m0, receiver.mc) + if equal0 == 0 { + t.Error("Receiver decryption should fail") + } + equal1 := bytes.Compare(sender.m1, receiver.mc) + if equal1 == 0 { + t.Error("Receiver decryption should fail") + } + } +} + +// Input: myGroup, the group we operate in +func testSimOT(t *testing.T, myGroup group.Group, choice int) { + var sender Sender + var receiver Receiver + + m0 := make([]byte, myGroup.Params().ScalarLength) + m1 := make([]byte, myGroup.Params().ScalarLength) + _, errRand := rand.Read(m0) + if errRand != nil { + panic(errRand) + } + _, errRand = rand.Read(m1) + if errRand != nil { + panic(errRand) + } + + errDec := simOT(myGroup, &sender, &receiver, m0, m1, choice, 0) + if errDec != nil { + t.Error("AES GCM Decryption failed") + } + + if choice == 0 { + equal0 := bytes.Compare(sender.m0, receiver.mc) + if equal0 != 0 { + t.Error("Receiver decryption failed") + } + } else { + equal1 := bytes.Compare(sender.m1, receiver.mc) + if equal1 != 0 { + t.Error("Receiver decryption failed") + } + } +} + +func benchmarSimOT(b *testing.B, myGroup group.Group) { + var sender Sender + var receiver Receiver + m0 := make([]byte, myGroup.Params().ScalarLength) + m1 := make([]byte, myGroup.Params().ScalarLength) + _, errRand := rand.Read(m0) + if errRand != nil { + panic(errRand) + } + _, errRand = rand.Read(m1) + if errRand != nil { + panic(errRand) + } + + for iter := 0; iter < b.N; iter++ { + errDec := simOT(myGroup, &sender, &receiver, m0, m1, iter%2, 0) + if errDec != nil { + b.Error("AES GCM Decryption failed") + } + } +} + +func benchmarkSimOTRound(b *testing.B, myGroup group.Group) { + var sender Sender + var receiver Receiver + m0 := make([]byte, myGroup.Params().ScalarLength) + m1 := make([]byte, myGroup.Params().ScalarLength) + _, errRand := rand.Read(m0) + if errRand != nil { + panic(errRand) + } + _, errRand = rand.Read(m1) + if errRand != nil { + panic(errRand) + } + + b.Run("Sender-Initialization", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sender.InitSender(myGroup, m0, m1, 0) + } + }) + + A := sender.InitSender(myGroup, m0, m1, 0) + + b.Run("Receiver-Round1", func(b *testing.B) { + for i := 0; i < b.N; i++ { + receiver.Round1Receiver(myGroup, 0, 0, A) + } + }) + + B := receiver.Round1Receiver(myGroup, 0, 0, A) + + b.Run("Sender-Round2", func(b *testing.B) { + for i := 0; i < b.N; i++ { + sender.Round2Sender(B) + } + }) + + e0, e1 := sender.Round2Sender(B) + + b.Run("Receiver-Round3", func(b *testing.B) { + for i := 0; i < b.N; i++ { + errDec := receiver.Round3Receiver(e0, e1, receiver.c) + if errDec != nil { + b.Error("Receiver-Round3 decryption failed") + } + } + }) + + errDec := receiver.Round3Receiver(e0, e1, receiver.c) + if errDec != nil { + b.Error("Receiver-Round3 decryption failed") + } + + // Confirm + equal0 := bytes.Compare(sender.m0, receiver.mc) + if equal0 != 0 { + b.Error("Receiver decryption failed") + } +} + +func TestSimOT(t *testing.T) { + t.Run("SimOT", func(t *testing.T) { + for i := 0; i < testSimOTCount; i++ { + currGroup := group.P256 + choice := i % 2 + testSimOT(t, currGroup, choice) + } + }) + t.Run("SimOTNegative", func(t *testing.T) { + for i := 0; i < testSimOTCount; i++ { + currGroup := group.P256 + choice := i % 2 + testNegativeSimOT(t, currGroup, choice) + } + }) +} + +func BenchmarkSimOT(b *testing.B) { + currGroup := group.P256 + benchmarSimOT(b, currGroup) +} + +func BenchmarkSimOTRound(b *testing.B) { + currGroup := group.P256 + benchmarkSimOTRound(b, currGroup) +} diff --git a/ot/simot/simotlocal.go b/ot/simot/simotlocal.go new file mode 100644 index 000000000..7c0dffa5d --- /dev/null +++ b/ot/simot/simotlocal.go @@ -0,0 +1,238 @@ +package simot + +import ( + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/subtle" + "errors" + "io" + + "github.com/cloudflare/circl/group" + "golang.org/x/crypto/sha3" +) + +const keyLength = 16 + +// AES GCM encryption, we don't need to pad because our input is fixed length +// Need to use authenticated encryption to defend against tampering on ciphertext +// Input: key, plaintext message +// Output: ciphertext +func aesEncGCM(key, plaintext []byte) []byte { + block, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + + aesgcm, err := cipher.NewGCM(block) + if err != nil { + panic(err.Error()) + } + + nonce := make([]byte, aesgcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + panic(err) + } + + ciphertext := aesgcm.Seal(nonce, nonce, plaintext, nil) + return ciphertext +} + +// AES GCM decryption +// Input: key, ciphertext message +// Output: plaintext +func aesDecGCM(key, ciphertext []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + panic(err) + } + aesgcm, err := cipher.NewGCM(block) + if err != nil { + panic(err.Error()) + } + nonceSize := aesgcm.NonceSize() + if len(ciphertext) < nonceSize { + return nil, errors.New("ciphertext too short") + } + + nonce, encryptedMessage := ciphertext[:nonceSize], ciphertext[nonceSize:] + + plaintext, err := aesgcm.Open(nil, nonce, encryptedMessage, nil) + + return plaintext, err +} + +// Initialization + +// Input: myGroup, the group we operate in +// Input: m0, m1 the 2 message of the sender +// Input: index, the index of this SimOT +// Output: A = [a]G, a the sender randomness +func (sender *Sender) InitSender(myGroup group.Group, m0, m1 []byte, index int) group.Element { + sender.a = myGroup.RandomNonZeroScalar(rand.Reader) + sender.k0 = make([]byte, keyLength) + sender.k1 = make([]byte, keyLength) + sender.m0 = m0 + sender.m1 = m1 + sender.index = index + sender.A = myGroup.NewElement() + sender.A.MulGen(sender.a) + sender.myGroup = myGroup + return sender.A.Copy() +} + +// Round 1 + +// ---- sender should send A to receiver ---- + +// Input: myGroup, the group we operate in +// Input: choice, the receiver choice bit +// Input: index, the index of this SimOT +// Input: A, from sender +// Output: B = [b]G if c == 0, B = A+[b]G if c == 1 (Implementation in constant time). b, the receiver randomness +func (receiver *Receiver) Round1Receiver(myGroup group.Group, choice int, index int, A group.Element) group.Element { + receiver.b = myGroup.RandomNonZeroScalar(rand.Reader) + receiver.c = choice + receiver.kR = make([]byte, keyLength) + receiver.index = index + receiver.A = A + receiver.myGroup = myGroup + + bG := myGroup.NewElement() + bG.MulGen(receiver.b) + AorI := myGroup.NewElement() + AorI.CMov(choice, A) + receiver.B = myGroup.NewElement() + receiver.B.Add(bG, AorI) + + return receiver.B.Copy() +} + +// Round 2 + +// ---- receiver should send B to sender ---- + +// Input: B from the receiver +// Output: e0, e1, encryption of m0 and m1 under key k0, k1 +func (sender *Sender) Round2Sender(B group.Element) ([]byte, []byte) { + sender.B = B + + aB := sender.myGroup.NewElement() + aB.Mul(sender.B, sender.a) + maA := sender.myGroup.NewElement() + maA.Mul(sender.A, sender.a) + maA.Neg(maA) + aBaA := sender.myGroup.NewElement() + aBaA.Add(aB, maA) + + // Hash the whole transcript A|B|... + AByte, errByte := sender.A.MarshalBinary() + if errByte != nil { + panic(errByte) + } + BByte, errByte := sender.B.MarshalBinary() + if errByte != nil { + panic(errByte) + } + aBByte, errByte := aB.MarshalBinary() + if errByte != nil { + panic(errByte) + } + hashByte0 := append(AByte, BByte...) + hashByte0 = append(hashByte0, aBByte...) + + s := sha3.NewShake128() + _, errWrite := s.Write(hashByte0) + if errWrite != nil { + panic(errWrite) + } + _, errRead := s.Read(sender.k0) + if errRead != nil { + panic(errRead) + } + + aBaAByte, errByte := aBaA.MarshalBinary() + if errByte != nil { + panic(errByte) + } + hashByte1 := append(AByte, BByte...) + hashByte1 = append(hashByte1, aBaAByte...) + s = sha3.NewShake128() + _, errWrite = s.Write(hashByte1) + if errWrite != nil { + panic(errWrite) + } + _, errRead = s.Read(sender.k1) + if errRead != nil { + panic(errRead) + } + + e0 := aesEncGCM(sender.k0, sender.m0) + sender.e0 = e0 + + e1 := aesEncGCM(sender.k1, sender.m1) + sender.e1 = e1 + + return sender.e0, sender.e1 +} + +// Round 3 + +// ---- sender should send e0, e1 to receiver ---- + +// Input: e0, e1: encryption of m0 and m1 from the sender +// Input: choice, choice bit of receiver +// Choose e0 or e1 based on choice bit in constant time +func (receiver *Receiver) Round3Receiver(e0, e1 []byte, choice int) error { + receiver.ec = make([]byte, len(e1)) + // If c == 1, copy e1 + subtle.ConstantTimeCopy(choice, receiver.ec, e1) + // If c == 0, copy e0 + subtle.ConstantTimeCopy(1-choice, receiver.ec, e0) + + AByte, errByte := receiver.A.MarshalBinary() + if errByte != nil { + panic(errByte) + } + BByte, errByte := receiver.B.MarshalBinary() + if errByte != nil { + panic(errByte) + } + bA := receiver.myGroup.NewElement() + bA.Mul(receiver.A, receiver.b) + bAByte, errByte := bA.MarshalBinary() + if errByte != nil { + panic(errByte) + } + // Hash the whole transcript so far + hashByte := append(AByte, BByte...) + hashByte = append(hashByte, bAByte...) + + s := sha3.NewShake128() + _, errWrite := s.Write(hashByte) + if errWrite != nil { + panic(errWrite) + } + _, errRead := s.Read(receiver.kR) // kR, decryption key of mc + if errRead != nil { + panic(errRead) + } + mc, errDec := aesDecGCM(receiver.kR, receiver.ec) + if errDec != nil { + return errDec + } + receiver.mc = mc + return nil +} + +func (receiver *Receiver) Returnmc() []byte { + return receiver.mc +} + +func (sender *Sender) Returne0e1() ([]byte, []byte) { + return sender.e0, sender.e1 +} + +func (sender *Sender) Returnm0m1() ([]byte, []byte) { + return sender.m0, sender.m1 +} diff --git a/ot/simot/simotparty.go b/ot/simot/simotparty.go new file mode 100644 index 000000000..d9ef87685 --- /dev/null +++ b/ot/simot/simotparty.go @@ -0,0 +1,29 @@ +package simot + +import "github.com/cloudflare/circl/group" + +type Sender struct { + index int // Indicate which OT + m0 []byte // The M0 message from sender + m1 []byte // The M1 message from sender + a group.Scalar // The randomness of the sender + A group.Element // [a]G + B group.Element // The random group element from the receiver + k0 []byte // The encryption key of M0 + k1 []byte // The encryption key of M1 + e0 []byte // The encryption of M0 under k0 + e1 []byte // The encryption of M1 under k1 + myGroup group.Group // The elliptic curve we operate in +} + +type Receiver struct { + index int // Indicate which OT + c int // The choice bit of the receiver + A group.Element // The random group element from the sender + b group.Scalar // The randomness of the receiver + B group.Element // B = [b]G if c == 0, B = A+[b]G if c == 1 + kR []byte // The decryption key of receiver + ec []byte // The encryption of mc + mc []byte // The decrypted message from sender + myGroup group.Group // The elliptic curve we operate in +}