simplify commit-reveal: only client sends commitment

streamline http handling and decrease RAM usage
This commit is contained in:
themighty1
2022-03-12 12:27:47 +03:00
parent f7f0ad5a24
commit b0fb0857e6
7 changed files with 400 additions and 635 deletions

View File

@@ -1,7 +1,6 @@
package evaluator
import (
"math"
"notary/meta"
u "notary/utils"
)
@@ -13,26 +12,22 @@ type Evaluator struct {
// they are meant to be read-only for evaluator
meta []*meta.Circuit
ttBlobs [][]byte // truth table blobs for each circuit
olBlobs [][]byte // output labels blobs for each circuit
}
func (e *Evaluator) Init(circuits []*meta.Circuit, c6Count int) {
e.C6Count = c6Count
e.meta = circuits
e.ttBlobs = make([][]byte, len(e.meta))
e.olBlobs = make([][]byte, len(e.meta))
}
// Evaluate evaluates a circuit number cNo
func (e *Evaluator) Evaluate(cNo int, notaryLabels, clientLabels,
truthTables, decodingTable []byte) []byte {
truthTables []byte) []byte {
type batch_t struct {
// wl is wire labels
wl *[][]byte
// tt is truth tables
tt *[]byte
// dt is decoding table
dt *[]byte
}
c := (e.meta)[cNo]
@@ -40,7 +35,6 @@ func (e *Evaluator) Evaluate(cNo int, notaryLabels, clientLabels,
nlBatch := u.SplitIntoChunks(notaryLabels, c.NotaryInputSize*16)
clBatch := u.SplitIntoChunks(clientLabels, c.ClientInputSize*16)
ttBatch := u.SplitIntoChunks(truthTables, c.AndGateCount*48)
dtBatch := u.SplitIntoChunks(decodingTable, int(math.Ceil(float64(c.OutputSize)/8)))
// exeCount is how many executions of this circuit we need
exeCount := []int{0, 1, 1, 1, 1, 1, e.C6Count, 1}[cNo]
@@ -49,41 +43,17 @@ func (e *Evaluator) Evaluate(cNo int, notaryLabels, clientLabels,
// put all input labels into wire labels
wireLabels := make([][]byte, c.WireCount)
copy(wireLabels, u.SplitIntoChunks(u.Concat(nlBatch[r], clBatch[r]), 16))
batch[r] = batch_t{&wireLabels, &ttBatch[r], &dtBatch[r]}
batch[r] = batch_t{&wireLabels, &ttBatch[r]}
}
var output []byte
encodedOutput := make([][]byte, exeCount)
for r := 0; r < exeCount; r++ {
plaintext := evaluate(c, batch[r].wl, batch[r].tt, batch[r].dt)
// plaintext has a padding in MSB to make it a multiple of 8 bits. We
// decompose into bits and drop the padding
outBits := u.BytesToBits(plaintext)[0:c.OutputSize]
// reverse output bits so that the values of the output be placed in
// the same order as they appear in the *.casm files
outBytes := e.parseOutputBits(cNo, outBits)
output = append(output, outBytes...)
encodedOutput[r] = evaluate(c, batch[r].wl, batch[r].tt)
}
return output
return u.Concat(encodedOutput...)
}
// parseOutputBits converts the output bits of the circuit into a flat slice
// of bytes so that output values are in the same order as they appear in the *.casm files
func (e *Evaluator) parseOutputBits(cNo int, outBits []int) []byte {
o := 0 // offset
var outBytes []byte
for _, v := range (e.meta)[cNo].OutputsSizes {
output := u.BitsToBytes(outBits[o : o+v])
outBytes = append(outBytes, output...)
o += v
}
if o != (e.meta)[cNo].OutputSize {
panic("o != e.g.Cs[cNo].OutputSize")
}
return outBytes
}
func evaluate(c *meta.Circuit, wireLabels *[][]byte, truthTables *[]byte,
decodingTable *[]byte) []byte {
func evaluate(c *meta.Circuit, wireLabels *[][]byte, truthTables *[]byte) []byte {
andGateIdx := 0
// gate type XOR==0 AND==1 INV==2
for i := 0; i < len(c.Gates); i++ {
@@ -99,16 +69,12 @@ func evaluate(c *meta.Circuit, wireLabels *[][]byte, truthTables *[]byte,
panic("Unknown gate")
}
}
// decode output labels
// get decoding table: LSB of label0 for each output wire
// return encoded output
outLSBs := make([]int, c.OutputSize)
for i := 0; i < c.OutputSize; i++ {
outLSBs[i] = int((*wireLabels)[c.WireCount-c.OutputSize+i][15]) & 1
}
encodings := u.BitsToBytes(outLSBs)
plaintext := u.XorBytes(*decodingTable, encodings)
return plaintext
return u.BitsToBytes(outLSBs)
}
func evaluateAnd(g meta.Gate, wireLabels *[][]byte, truthTables *[]byte, andGateIdx int) {

View File

@@ -25,16 +25,16 @@ type gc struct {
// Blob is what is returned when gc is read from disk
type Blob struct {
Il *[]byte
// we dont return bytes of tt and dt because we gonna be streaming the file
// we dont return bytes of tt because we gonna be streaming the file
// directly into the HTTP response to save memory
TtFile *os.File
DtFile *os.File
Dt *[]byte
}
type GarbledPool struct {
// gPDirPath is full path to the garbled pool dir
gPDirPath string
// AES-GCM keys to encrypt/authenticate circuits' labels.
// AES-GCM keys to encrypt/authenticate circuits' blob.
// We need to encrypt them in case we want to store them outside the enclave.
// When the encryption key changes, older keys are kept because we still
// have labels on disk encrypted with old keys.
@@ -55,7 +55,7 @@ type GarbledPool struct {
// the amount of c5 circuits will be poolSize*100 because on average one
// session needs that many garbled c5 circuits
poolSize int
// Circuits's count starts from 1
// Circuits contains metainfo for each circuit. Circuit count starts from 1
Circuits []*meta.Circuit
grb garbler.Garbler
// noSandbox is set to true when not running in a sandboxed environment
@@ -112,12 +112,13 @@ func (g *GarbledPool) Init(noSandbox bool) {
}
// returns 1 garbling of each circuit and c5Count garblings for circuit 5
func (g *GarbledPool) GetBlobs(c6Count int) []Blob {
func (g *GarbledPool) GetBlobs(c6Count int) [][]Blob {
if c6Count > 1026 {
panic("c6Count > 1026")
}
var allBlobs []Blob
// we don't use index 0 for clarity, count starts from 1
allBlobs := make([][]Blob, len(g.Circuits))
// fetch blobs
for i := 1; i < len(g.Circuits); i++ {
iStr := strconv.Itoa(i)
@@ -140,7 +141,7 @@ func (g *GarbledPool) GetBlobs(c6Count int) []Blob {
g.pool[iStr] = g.pool[iStr][1:]
g.Unlock()
blob := g.fetchBlob(iStr, gc)
allBlobs = append(allBlobs, blob)
allBlobs[i] = append(allBlobs[i], blob)
}
}
return allBlobs
@@ -244,12 +245,16 @@ func (g *GarbledPool) monitor() {
func (g *GarbledPool) saveBlob(path string, il *[]byte, tt *[]byte, dt *[]byte) {
var ilToWrite *[]byte
// we only encrypt input labels
var dtToWrite *[]byte
// we encrypt input labels and decoding table
if !g.noSandbox {
ilEnc := u.AESGCMencrypt(g.key, *il)
ilToWrite = &ilEnc
dtEnc := u.AESGCMencrypt(g.key, *dt)
dtToWrite = &dtEnc
} else {
ilToWrite = il
dtToWrite = dt
}
err := os.WriteFile(path+"_il", *ilToWrite, 0644)
if err != nil {
@@ -259,13 +264,14 @@ func (g *GarbledPool) saveBlob(path string, il *[]byte, tt *[]byte, dt *[]byte)
if err != nil {
panic(err)
}
err = os.WriteFile(path+"_dt", *dt, 0644)
err = os.WriteFile(path+"_dt", *dtToWrite, 0644)
if err != nil {
panic(err)
}
}
// fetches the blob from disk and deletes it
// fetches the blob from disk and deletes il and dt. tt will be deleted later
// by the caller.
func (g *GarbledPool) fetchBlob(circuitNo string, c gc) Blob {
fullPath := filepath.Join(g.gPDirPath, "c"+circuitNo, c.id)
il, err := os.ReadFile(fullPath + "_il")
@@ -276,7 +282,16 @@ func (g *GarbledPool) fetchBlob(circuitNo string, c gc) Blob {
if err != nil {
panic(err)
}
// only the file handle of truth tables and decoding tables is returned,
dt, err2 := os.ReadFile(fullPath + "_dt")
if err2 != nil {
panic(err2)
}
err = os.Remove(fullPath + "_dt")
if err != nil {
panic(err)
}
// only the file handle of truth tables is returned,
// so that the file could be streamed (avoiding a full copy into memory)
// The session which receives this handle will be responsible for
// deleting the file
@@ -284,17 +299,16 @@ func (g *GarbledPool) fetchBlob(circuitNo string, c gc) Blob {
if err3 != nil {
panic(err3)
}
dtFile, err4 := os.Open(fullPath + "_dt")
if err4 != nil {
panic(err4)
}
var ilToReturn = &il
var dtToReturn = &dt
if !g.noSandbox {
// decrypt data from disk when in a sandbox
ilDec := u.AESGCMdecrypt(g.keys[c.keyIdx], il)
ilToReturn = &ilDec
dtDec := u.AESGCMdecrypt(g.keys[c.keyIdx], dt)
dtToReturn = &dtDec
}
return Blob{ilToReturn, ttFile, dtFile}
return Blob{ilToReturn, ttFile, dtToReturn}
}
// Convert the circuits from the "Bristol fashion" format into a compact

View File

@@ -16,34 +16,32 @@ type Garbler struct {
Cs []CData
}
// CData is circuit's data
// CData is data for one circuit
type CData struct {
Il []byte // input labels
// InputBits start with least input bit at index [0]
InputBits []int // notary's input for this circuit
// Il contains a flat slice of all input labels for all executions of
// one circuit
Il []byte
// InputBits is notary's input for this circuit. Starts with the least
// input bit at index [0].
InputBits []int
// Masks are notary's masks. They are inputs to the circuit. Their purpose
// is to mask the circuit's output. Mask numbering starts with 1 for
// convenience. Consult circuits/*.casm files for description of what each
// mask does.
Masks [][]byte
Meta *meta.Circuit
}
// Init puts input labels into correspondign circuits and creates masks for
// notary's inputs to the circuits.
// ilBlobs contains slices of input labels for each execution
func (g *Garbler) Init(ilBlobs []*[]byte, circuits []*meta.Circuit, c6Count int) {
// il contains input labels for each execution of each circuit
func (g *Garbler) Init(il [][][]byte, circuits []*meta.Circuit, c6Count int) {
g.C6Count = c6Count
g.Cs = make([]CData, len(circuits))
for i := 1; i < len(g.Cs); i++ {
if i < 6 {
g.Cs[i].Il = *ilBlobs[i-1]
} else if i == 6 {
g.Cs[i].Il = u.ConcatP(ilBlobs[5 : 5+c6Count]...)
} else if i > 6 {
g.Cs[i].Il = *ilBlobs[c6Count-1+i-1]
}
g.Cs[i].Il = u.Concat(il[i]...)
g.Cs[i].Meta = circuits[i]
// mask numbering starts at 1 for convenience
// consult circuits/*.casm files for what each mask does
if i == 1 {
g.Cs[i].Masks = make([][]byte, 2)
g.Cs[i].Masks[1] = u.GetRandom(32)

View File

@@ -21,9 +21,9 @@ import (
type KeyManager struct {
sync.Mutex
// Blob contains validFrom|validUntil|pubkey|signature
// KeyData contains validFrom|validUntil|pubkey|signature
// the client will verify the signature (made with the masterKey)
Blob []byte
KeyData []byte
// PrivKey is the ephemeral key used to sign a session. Also used
// in ECDH with the the client to derive symmetric keys to encrypt the communication
PrivKey *ecdsa.PrivateKey
@@ -40,6 +40,19 @@ func (k *KeyManager) Init() {
go k.rotateEphemeralKeys()
}
// GetActiveKey returns the currently active signing key as well as KeyData
// associated with it
func (k *KeyManager) GetActiveKey() (ecdsa.PrivateKey, []byte) {
// copying data so that it doesn't change from under us if
// ephemeral key happens to change while this session is running
k.Lock()
keyData := make([]byte, len(k.KeyData))
copy(keyData, k.KeyData)
key := *k.PrivKey
k.Unlock()
return key, keyData
}
// generateMasterKey generates a P-256 master key. The corresponding public key
// in PEM format is written to disk
func (k *KeyManager) generateMasterKey() {
@@ -94,7 +107,7 @@ func (k *KeyManager) rotateEphemeralKeys() {
signature := u.ECDSASign(k.masterKey, validFrom, validUntil, pubkey)
blob := u.Concat(validFrom, validUntil, pubkey, signature)
k.Lock()
k.Blob = blob
k.KeyData = blob
k.PrivKey = newKey
k.Unlock()
}

View File

@@ -67,49 +67,43 @@ func destroyOnPanic(s *session.Session) {
s.DestroyChan <- s.Sid
}
func init1(w http.ResponseWriter, req *http.Request) {
log.Println("in init1", req.RemoteAddr)
s := sm.AddSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
func httpHandler(w http.ResponseWriter, req *http.Request) {
// sessionId is the part of the URL after ?
sessionId := string(req.URL.RawQuery)
// command is URL path without the leading /
command := req.URL.Path[1:]
log.Println("got request ", command, " from ", req.RemoteAddr)
var out []byte
if command == "init1" {
s := sm.AddSession(sessionId)
s.Gp = gp
// copying data so that it doesn't change from under us if
// ephemeral key happens to change while this session is running
km.Lock()
blob := make([]byte, len(km.Blob))
copy(blob, km.Blob)
key := *km.PrivKey
km.Unlock()
out := s.Init1(body, blob, key)
writeResponse(out, w)
key, keyData := km.GetActiveKey()
s.SigningKey = key
// keyData is sent to Client unencrypted
out = append(out, keyData...)
}
func init2(w http.ResponseWriter, req *http.Request) {
log.Println("in init2", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
s := sm.GetSession(sessionId)
defer destroyOnPanic(s)
method := sm.GetMethod(command, sessionId)
body := readBody(req)
out := s.Init2(body)
out = append(out, method(body)...)
writeResponse(out, w)
if command == "commitHash" {
// this was the final message of the session. Destroying the session...
s.DestroyChan <- s.Sid
}
}
// getBlob is called when user wants to download garbled circuits
func getBlob(w http.ResponseWriter, req *http.Request) {
log.Println("in getBlob", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
tt, dt := s.GetBlob(body)
// send headers first
fileHandles := s.GetBlob(body)
writeResponse(nil, w)
// stream decoding table directly from file
for _, f := range dt {
_, err := io.Copy(w, f)
if err != nil {
panic("err != nil")
}
}
// stream decoding table directly from file
for _, f := range tt {
// stream directly from file
for _, f := range fileHandles {
_, err := io.Copy(w, f)
if err != nil {
panic("err != nil")
@@ -117,6 +111,7 @@ func getBlob(w http.ResponseWriter, req *http.Request) {
}
}
// setBlob is called when user wants to upload garbled circuits
func setBlob(w http.ResponseWriter, req *http.Request) {
log.Println("in setBlob", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
@@ -125,298 +120,9 @@ func setBlob(w http.ResponseWriter, req *http.Request) {
writeResponse(out, w)
}
func getUploadProgress(w http.ResponseWriter, req *http.Request) {
log.Println("in getUploadProgress", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
out := s.GetUploadProgress()
writeResponse(out, w)
}
func step1(w http.ResponseWriter, req *http.Request) {
log.Println("in step1", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.Step1(body)
writeResponse(out, w)
}
func step2(w http.ResponseWriter, req *http.Request) {
log.Println("in step2", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.Step2(body)
writeResponse(out, w)
}
func step3(w http.ResponseWriter, req *http.Request) {
log.Println("in step3", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.Step3(body)
writeResponse(out, w)
}
func step4(w http.ResponseWriter, req *http.Request) {
log.Println("in step4", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.Step4(body)
writeResponse(out, w)
}
func c1_step1(w http.ResponseWriter, req *http.Request) {
log.Println("in c1_step1", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C1_step1(body)
writeResponse(out, w)
}
func c1_step2(w http.ResponseWriter, req *http.Request) {
log.Println("in c1_step2", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C1_step2(body)
writeResponse(out, w)
}
func c1_step3(w http.ResponseWriter, req *http.Request) {
log.Println("in c1_step3", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C1_step3(body)
writeResponse(out, w)
}
func c1_step4(w http.ResponseWriter, req *http.Request) {
log.Println("in c1_step4", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C1_step4(body)
writeResponse(out, w)
}
func c1_step5(w http.ResponseWriter, req *http.Request) {
log.Println("in c1_step5", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C1_step5(body)
writeResponse(out, w)
}
func c2_step1(w http.ResponseWriter, req *http.Request) {
log.Println("in c2_step1", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C2_step1(body)
writeResponse(out, w)
}
func c2_step2(w http.ResponseWriter, req *http.Request) {
log.Println("in c2_step2", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C2_step2(body)
writeResponse(out, w)
}
func c2_step3(w http.ResponseWriter, req *http.Request) {
log.Println("in c2_step3", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C2_step3(body)
writeResponse(out, w)
}
func c2_step4(w http.ResponseWriter, req *http.Request) {
log.Println("in c2_step4", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C2_step4(body)
writeResponse(out, w)
}
func c3_step1(w http.ResponseWriter, req *http.Request) {
log.Println("in c3_step1", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C3_step1(body)
writeResponse(out, w)
}
func c3_step2(w http.ResponseWriter, req *http.Request) {
log.Println("in c3_step2", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C3_step2(body)
writeResponse(out, w)
}
func c4_step1(w http.ResponseWriter, req *http.Request) {
log.Println("in c4_step1", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C4_step1(body)
writeResponse(out, w)
}
func c4_step2(w http.ResponseWriter, req *http.Request) {
log.Println("in c4_step2", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C4_step2(body)
writeResponse(out, w)
}
func c4_step3(w http.ResponseWriter, req *http.Request) {
log.Println("in c4_step3", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C4_step3(body)
writeResponse(out, w)
}
func c5_pre1(w http.ResponseWriter, req *http.Request) {
log.Println("in c5_pre1", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C5_pre1(body)
writeResponse(out, w)
}
func c5_step1(w http.ResponseWriter, req *http.Request) {
log.Println("in c5_step1", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C5_step1(body)
writeResponse(out, w)
}
func c5_step2(w http.ResponseWriter, req *http.Request) {
log.Println("in c5_step2", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C5_step2(body)
writeResponse(out, w)
}
func c5_step3(w http.ResponseWriter, req *http.Request) {
log.Println("in c5_step3", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C5_step3(body)
writeResponse(out, w)
}
func c6_step1(w http.ResponseWriter, req *http.Request) {
log.Println("in c6_step1", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C6_step1(body)
writeResponse(out, w)
}
func c6_step2(w http.ResponseWriter, req *http.Request) {
log.Println("in c6_step2", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C6_step2(body)
writeResponse(out, w)
}
func c7_step1(w http.ResponseWriter, req *http.Request) {
log.Println("in c7_step1", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C7_step1(body)
writeResponse(out, w)
}
func c7_step2(w http.ResponseWriter, req *http.Request) {
log.Println("in c7_step2", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.C7_step2(body)
writeResponse(out, w)
}
func checkC7Commit(w http.ResponseWriter, req *http.Request) {
log.Println("in checkC7Commit", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.CheckC7Commit(body)
writeResponse(out, w)
}
func ghash_step1(w http.ResponseWriter, req *http.Request) {
log.Println("in ghash_step1", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.Ghash_step1(body)
writeResponse(out, w)
}
func ghash_step2(w http.ResponseWriter, req *http.Request) {
log.Println("in ghash_step2", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.Ghash_step2(body)
writeResponse(out, w)
}
func ghash_step3(w http.ResponseWriter, req *http.Request) {
log.Println("in ghash_step3", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.Ghash_step3(body)
writeResponse(out, w)
}
func commitHash(w http.ResponseWriter, req *http.Request) {
log.Println("in commitHash", req.RemoteAddr)
s := sm.GetSession(string(req.URL.RawQuery))
defer destroyOnPanic(s)
body := readBody(req)
out := s.CommitHash(body)
writeResponse(out, w)
s.DestroyChan <- s.Sid
}
// when notary starts we expect the admin to upload a URLFetcher document
// it can be uploaded e.g. with:
// curl --data-binary '@URLFetcherDoc' 127.0.0.1:10012/setURLFetcherDoc
func awaitURLFetcherDoc() {
serverMux := http.NewServeMux()
srv := &http.Server{Addr: ":10012", Handler: serverMux}
@@ -443,7 +149,7 @@ func getPubKey(w http.ResponseWriter, req *http.Request) {
}
// initially the circuits are in the human-readable c*.casm format; assemble.js
// converts them into a "Bristol fashion" format and write to disk c*.out files
// converts them into a "Bristol fashion" format and writes to disk c*.out files
func assembleCircuits() {
curDir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
baseDir := filepath.Dir(curDir)
@@ -457,11 +163,16 @@ func assembleCircuits() {
log.Println("Error. Could not run: node assemble.js. Please make sure that node is installed on your system.")
os.Exit(1)
}
log.Println("Finished assembling circuits.")
}
}
func main() {
// uncomment the below to profile the process's RAM usage
// install with: go get github.com/pkg/profile
// then run: curl http://localhost:8080/debug/pprof/heap > heap
// go tool pprof -png heap
// defer profile.Start(profile.MemProfile).Stop()
// go func() {
// http.ListenAndServe(":8080", nil)
@@ -487,64 +198,11 @@ func main() {
// can be useful when debugging sandboxed notary
http.HandleFunc("/getPubKey", getPubKey)
http.HandleFunc("/init1", init1)
http.HandleFunc("/init2", init2)
http.HandleFunc("/getBlob", getBlob)
http.HandleFunc("/setBlob", setBlob)
http.HandleFunc("/getUploadProgress", getUploadProgress)
// step1 thru step4 deal with Paillier 2PC
http.HandleFunc("/step1", step1)
http.HandleFunc("/step2", step2)
http.HandleFunc("/step3", step3)
http.HandleFunc("/step4", step4)
// c1_step1 thru c1_step1 deal with TLS Handshake
http.HandleFunc("/c1_step1", c1_step1)
http.HandleFunc("/c1_step2", c1_step2)
http.HandleFunc("/c1_step3", c1_step3)
http.HandleFunc("/c1_step4", c1_step4)
http.HandleFunc("/c1_step5", c1_step5)
// c2_step1 thru c2_step4 deal with TLS Handshake
http.HandleFunc("/c2_step1", c2_step1)
http.HandleFunc("/c2_step2", c2_step2)
http.HandleFunc("/c2_step3", c2_step3)
http.HandleFunc("/c2_step4", c2_step4)
// c3_step1 thru c4_step3 deal with TLS Handshake and also prepare data
// needed to send Client Finished
http.HandleFunc("/c3_step1", c3_step1)
http.HandleFunc("/c3_step2", c3_step2)
http.HandleFunc("/c4_step1", c4_step1)
http.HandleFunc("/c4_step2", c4_step2)
http.HandleFunc("/c4_step3", c4_step3)
// c5_pre1 thru c5_step3 check Server Finished
http.HandleFunc("/c5_pre1", c5_pre1)
http.HandleFunc("/c5_step1", c5_step1)
http.HandleFunc("/c5_step2", c5_step2)
http.HandleFunc("/c5_step3", c5_step3)
// c6_step1 thru c6_step2 prepare encrypted counter blocks for the
// client's request to the webserver
http.HandleFunc("/c6_step1", c6_step1)
http.HandleFunc("/c6_step2", c6_step2)
// c7_step1 thru c7_step2 prepare the GCTR block needed to compute the MAC
// for the client's request
http.HandleFunc("/c7_step1", c7_step1)
http.HandleFunc("/c7_step2", c7_step2)
http.HandleFunc("/checkC7Commit", checkC7Commit)
// steps ghash_step1 thru ghash_step3 compute the GHASH output needed to
// compute the MAC for the client's request
http.HandleFunc("/ghash_step1", ghash_step1)
http.HandleFunc("/ghash_step2", ghash_step2)
http.HandleFunc("/ghash_step3", ghash_step3)
http.HandleFunc("/commitHash", commitHash)
// all the other request will end up in the httpHandler
http.HandleFunc("/", httpHandler)
http.ListenAndServe("0.0.0.0:10011", nil)
}

View File

@@ -7,7 +7,6 @@ import (
"encoding/binary"
"io"
"log"
"math"
"math/big"
"notary/evaluator"
"notary/garbled_pool"
@@ -72,8 +71,8 @@ type Session struct {
notaryKey []byte
// clientKey is a symmetric key used to decrypt messages FROM the client
clientKey []byte
// signingKey is an ephemeral key used to sign the notarization session
signingKey *ecdsa.PrivateKey
// SigningKey is an ephemeral key used to sign the notarization session
SigningKey ecdsa.PrivateKey
// StorageDir is where the blobs from the client are stored
StorageDir string
// msgsSeen contains a list of all messages seen from the client
@@ -84,16 +83,21 @@ type Session struct {
PmsOuterHashState []byte
// MsOuterHashState is the state of the outer hash of HMAC needed to compute the MS
MsOuterHashState []byte
// Commitment is the hash of plaintext output for each circuit
Commitment [][]byte
// Salt is used to salt Commitment before sending it to the client
Salt [][]byte
// hisCommitment is client's salted commitment for each circuit
hisCommitment [][]byte
// encodedOutput is notary's encoded output for each circuit
encodedOutput [][]byte
// c6CheckValue is encoded outputs and decoding table which must the sent to
// Client as part of dual execution garbling. We store it here until Client
// sends her commitment. Then we send it out.
c6CheckValue []byte
// meta contains information about circuits
meta []*meta.Circuit
// Tt/Dt are file handles for truth tables/decoding tables which are used
// Tt are file handles for truth tables which are used
// to stream directly to the HTTP response (saving memory)
Dt []*os.File
Tt []*os.File
Tt [][]*os.File
// dt are decoding tables for each execution of each garbled circuit
dt [][][]byte
// streamCounter is used when client uploads his blob to the notary
streamCounter *StreamCounter
// Gp is used to access the garbled pool
@@ -108,7 +112,7 @@ type Session struct {
// Init1 is the first message from the client. It starts Oblivious Transfer
// setup and we also initialize all of Session's structures.
func (s *Session) Init1(body, blob []byte, signingKey ecdsa.PrivateKey) []byte {
func (s *Session) Init1(body []byte) []byte {
s.sequenceCheck(1)
s.g = new(garbler.Garbler)
s.e = new(evaluator.Evaluator)
@@ -116,10 +120,9 @@ func (s *Session) Init1(body, blob []byte, signingKey ecdsa.PrivateKey) []byte {
s.otR = new(ot.OTReceiver)
s.p2pc = new(paillier2pc.Paillier2PC)
s.ghash = new(ghash.GHASH)
s.signingKey = &signingKey
// the first 64 bytes are client pubkey for ECDH
o := 0
s.clientKey, s.notaryKey = s.getSymmetricKeys(body[o:o+64], &signingKey)
s.clientKey, s.notaryKey = s.getSymmetricKeys(body[o:o+64], &s.SigningKey)
o += 64
c6Count := int(new(big.Int).SetBytes(body[o : o+2]).Uint64())
o += 2
@@ -153,26 +156,33 @@ func (s *Session) Init1(body, blob []byte, signingKey ecdsa.PrivateKey) []byte {
panic(err)
}
// get already garbled circuits
// get already garbled circuits ...
blobs := s.Gp.GetBlobs(c6Count)
// separate into input labels, truth tables, decoding table
il := make([]*[]byte, len(blobs))
s.Tt = make([]*os.File, len(blobs))
s.Dt = make([]*os.File, len(blobs))
for i := 0; i < len(blobs); i++ {
il[i] = blobs[i].Il
s.Tt[i] = blobs[i].TtFile
s.Dt[i] = blobs[i].DtFile
// and separate into input labels, truth tables, decoding table
il := make([][][]byte, len(s.Gp.Circuits))
s.Tt = make([][]*os.File, len(s.Gp.Circuits))
s.dt = make([][][]byte, len(s.Gp.Circuits))
// depending on the number of circuit executions, there may be more than
// one Blob for every circuit
for i := 1; i < len(s.Gp.Circuits); i++ {
il[i] = make([][]byte, len(blobs[i]))
s.Tt[i] = make([]*os.File, len(blobs[i]))
s.dt[i] = make([][]byte, len(blobs[i]))
for j, blob := range blobs[i] {
il[i][j] = *blob.Il
s.Tt[i][j] = blob.TtFile
s.dt[i][j] = *blob.Dt
}
}
s.meta = s.Gp.Circuits
s.g.Init(il, s.meta, c6Count)
s.e.Init(s.meta, c6Count)
s.Salt = make([][]byte, len(s.g.Cs))
s.Commitment = make([][]byte, len(s.g.Cs))
s.hisCommitment = make([][]byte, len(s.g.Cs))
s.encodedOutput = make([][]byte, len(s.g.Cs))
s.p2pc.Init()
return u.Concat(blob, s.encryptToClient(u.Concat(A, seedCommit, allBs,
senderSeedShare)))
return s.encryptToClient(u.Concat(A, seedCommit, allBs, senderSeedShare))
}
// continue initialization. Setting up the Oblivious Transfer.
@@ -201,10 +211,18 @@ func (s *Session) Init2(encrypted []byte) []byte {
return s.encryptToClient(u.Concat(encryptedColumns, receiverSeedShare, x, t))
}
// GetBlobChunk returns file handles to truth tables and decoding table
func (s *Session) GetBlob(encrypted []byte) ([]*os.File, []*os.File) {
// GetBlob returns file handles to truth tables
func (s *Session) GetBlob(encrypted []byte) []*os.File {
s.sequenceCheck(3)
return s.Tt, s.Dt
// flatten into one slice
var flat []*os.File
for _, sliceOfFiles := range s.Tt {
if len(sliceOfFiles) == 0 {
continue
}
flat = append(flat, sliceOfFiles...)
}
return flat
}
// SetBlobChunk stores a blob from the client.
@@ -224,7 +242,7 @@ func (s *Session) SetBlob(respBody io.ReadCloser) []byte {
return nil
}
func (s *Session) GetUploadProgress() []byte {
func (s *Session) GetUploadProgress(dummy []byte) []byte {
// special case. This message may be repeated many times
s.sequenceCheck(100)
bytes := make([]byte, 4)
@@ -273,22 +291,18 @@ func (s *Session) C1_step1(encrypted []byte) []byte {
func (s *Session) C1_step2(encrypted []byte) []byte {
s.sequenceCheck(10)
body := s.decryptFromClient(encrypted)
ttBlob, dtBlob := s.RetrieveBlobsForNotary(1)
notaryLabels, clientLabels := s.c_step2(1, body)
output := s.e.Evaluate(1, notaryLabels, clientLabels, ttBlob, dtBlob)
s.Commitment[1] = u.Sha256(output)
s.Salt[1] = u.GetRandom(32)
hash := u.Sha256(u.Concat(s.Commitment[1], s.Salt[1]))
// unmask the output
s.PmsOuterHashState = u.XorBytes(output[0:32], s.g.Cs[1].Masks[1])
return s.encryptToClient(hash)
return s.encryptToClient(s.common_step2(1, body))
}
// [REF 1] Step 4. N computes a1 and passes it to C.
func (s *Session) C1_step3(encrypted []byte) []byte {
s.sequenceCheck(11)
body := s.decryptFromClient(encrypted)
a1 := u.FinishHash(s.PmsOuterHashState, body)
output := s.processDecommit(1, body[:len(body)-32])
hisInnerHash := body[len(body)-32:]
// unmask the output
s.PmsOuterHashState = u.XorBytes(output[0:32], s.g.Cs[1].Masks[1])
a1 := u.FinishHash(s.PmsOuterHashState, hisInnerHash)
return s.encryptToClient(a1)
}
@@ -321,23 +335,19 @@ func (s *Session) C2_step1(encrypted []byte) []byte {
func (s *Session) C2_step2(encrypted []byte) []byte {
s.sequenceCheck(15)
body := s.decryptFromClient(encrypted)
ttBlob, olBlob := s.RetrieveBlobsForNotary(2)
notaryLabels, clientLabels := s.c_step2(2, body)
output := s.e.Evaluate(2, notaryLabels, clientLabels, ttBlob, olBlob)
s.Commitment[2] = u.Sha256(output)
s.Salt[2] = u.GetRandom(32)
hash := u.Sha256(u.Concat(s.Commitment[2], s.Salt[2]))
// unmask the output
s.MsOuterHashState = u.XorBytes(output[0:32], s.g.Cs[2].Masks[1])
return s.encryptToClient(hash)
return s.encryptToClient(s.common_step2(2, body))
}
// [REF 1] Step 14 and Step 21. N computes a1 and a1 and sends it to C.
func (s *Session) C2_step3(encrypted []byte) []byte {
s.sequenceCheck(16)
body := s.decryptFromClient(encrypted)
a1inner := body[:32]
a1inner_vd := body[32:64]
output := s.processDecommit(2, body[:len(body)-64])
a1inner := body[len(body)-64 : len(body)-32]
a1inner_vd := body[len(body)-32:]
// unmask the output
s.MsOuterHashState = u.XorBytes(output[0:32], s.g.Cs[2].Masks[1])
a1 := u.FinishHash(s.MsOuterHashState, a1inner)
a1_vd := u.FinishHash(s.MsOuterHashState, a1inner_vd)
return s.encryptToClient(u.Concat(a1, a1_vd))
@@ -380,21 +390,20 @@ func (s *Session) C3_step1(encrypted []byte) []byte {
func (s *Session) C3_step2(encrypted []byte) []byte {
s.sequenceCheck(19)
body := s.decryptFromClient(encrypted)
ttBlob, olBlob := s.RetrieveBlobsForNotary(3)
notaryLabels, clientLabels := s.c_step2(3, body)
output := s.e.Evaluate(3, notaryLabels, clientLabels, ttBlob, olBlob)
// notary doesn't need to parse the output of the circuit, since we
// already know what out TLS key shares are
s.Commitment[3] = u.Sha256(output)
s.Salt[3] = u.GetRandom(32)
hash := u.Sha256(u.Concat(s.Commitment[3], s.Salt[3]))
return s.encryptToClient(hash)
return s.encryptToClient(s.common_step2(3, body))
}
// [REF 1] Step 18.
func (s *Session) C4_step1(encrypted []byte) []byte {
s.sequenceCheck(20)
body := s.decryptFromClient(encrypted)
// to save a round-trip, circuit 3 piggy-backs on this message to parse the
// decommitment. Notary doesn't need to parse the output of the circuit,
// since we already know what out TLS key shares are
decommitSize := len(s.encodedOutput[3]) + len(u.Concat(s.dt[3]...)) + 32
s.processDecommit(3, body[:decommitSize])
body = body[decommitSize:]
g := s.g
s.setCircuitInputs(4,
s.swkShare,
@@ -404,14 +413,14 @@ func (s *Session) C4_step1(encrypted []byte) []byte {
g.Cs[4].Masks[1],
g.Cs[4].Masks[2])
hisOtReq := s.c_step1A(4, body)
hisOtReq := body
// instead of the usual c_step1B, we have a special case
otResp, encryptedLabels := s.c4_step1B(hisOtReq)
out := s.c_step1C(4, otResp)
otResp, encryptedLabels := s.c4_step1A(hisOtReq)
out := s.c_step1B(4, otResp)
return s.encryptToClient(u.Concat(out, encryptedLabels))
}
func (s *Session) c4_step1B(hisOtReq []byte) ([]byte, []byte) {
func (s *Session) c4_step1A(hisOtReq []byte) ([]byte, []byte) {
// We need to make sure that the same input labels which we give
// to the client for c4's client_write_key (cwk) and client_write_iv
// (civ) will also be given for all invocations of circuit 6. This ensures
@@ -492,20 +501,11 @@ func (s *Session) c4_step1B(hisOtReq []byte) ([]byte, []byte) {
return newOtResp, encryptedLabels
}
// [REF 1] Step 18. Notary doesn't need to parse the circuit's output because
// the masks that he inputted become his TLS keys' shares.
// [REF 1] Step 18.
func (s *Session) C4_step2(encrypted []byte) []byte {
s.sequenceCheck(21)
body := s.decryptFromClient(encrypted)
ttBlob, olBlob := s.RetrieveBlobsForNotary(4)
notaryLabels, clientLabels := s.c_step2(4, body)
output := s.e.Evaluate(4, notaryLabels, clientLabels, ttBlob, olBlob)
// notary doesn't need to parse the output of the circuit, since we
// already know what out TLS key shares are
s.Commitment[4] = u.Sha256(output)
s.Salt[4] = u.GetRandom(32)
hash := u.Sha256(u.Concat(s.Commitment[4], s.Salt[4]))
return s.encryptToClient(hash)
return s.encryptToClient(s.common_step2(4, body))
}
// compute MAC for Client_Finished using Oblivious Transfer
@@ -514,9 +514,11 @@ func (s *Session) C4_step2(encrypted []byte) []byte {
func (s *Session) C4_step3(encrypted []byte) []byte {
s.sequenceCheck(22)
body := s.decryptFromClient(encrypted)
// Notary doesn't need to parse circuit's 4 output because
// the masks that he inputted become his TLS keys' shares.
s.processDecommit(4, body[:len(body)-(16+33)])
body = body[len(body)-(16+33):]
g := s.g
u.Assert(len(body) == 16+33)
o := 0
encCF := body[o : o+16]
o += 16
@@ -578,14 +580,13 @@ func (s *Session) C5_pre1(encrypted []byte) []byte {
func (s *Session) C5_step1(encrypted []byte) []byte {
s.sequenceCheck(24)
body := s.decryptFromClient(encrypted)
g := s.g
s.setCircuitInputs(5,
s.MsOuterHashState,
s.swkShare,
s.sivShare,
g.Cs[5].Masks[1],
g.Cs[5].Masks[2])
u.Assert(len(g.Cs[5].InputBits)/8 == 84)
s.g.Cs[5].Masks[1],
s.g.Cs[5].Masks[2])
u.Assert(len(s.g.Cs[5].InputBits)/8 == 84)
out := s.c_step1(5, body)
return s.encryptToClient(out)
}
@@ -594,13 +595,7 @@ func (s *Session) C5_step1(encrypted []byte) []byte {
func (s *Session) C5_step2(encrypted []byte) []byte {
s.sequenceCheck(25)
body := s.decryptFromClient(encrypted)
ttBlob, olBlob := s.RetrieveBlobsForNotary(5)
notaryLabels, clientLabels := s.c_step2(5, body)
output := s.e.Evaluate(5, notaryLabels, clientLabels, ttBlob, olBlob)
s.Commitment[5] = u.Sha256(output)
s.Salt[5] = u.GetRandom(32)
hash := u.Sha256(u.Concat(s.Commitment[5], s.Salt[5]))
return s.encryptToClient(hash)
return s.encryptToClient(s.common_step2(5, body))
}
// compute MAC for Server_Finished using Oblivious Transfer
@@ -608,9 +603,9 @@ func (s *Session) C5_step2(encrypted []byte) []byte {
func (s *Session) C5_step3(encrypted []byte) []byte {
s.sequenceCheck(26)
body := s.decryptFromClient(encrypted)
s.processDecommit(5, body[:len(body)-(16+33)])
body = body[len(body)-(16+33):]
g := s.g
u.Assert(len(body) == 16+33)
o := 0
encSF := body[o : o+16]
o += 16
@@ -657,7 +652,7 @@ func (s *Session) C6_step1(encrypted []byte) []byte {
}
s.setCircuitInputs(6, allInputs...)
hisOtReq := s.c_step1A(6, body)
hisOtReq := body
// ---------------------------------------
// instead of the usual c_step1B, we have a special case:
// we need to remove all the labels corresponding to client_write_key
@@ -674,25 +669,34 @@ func (s *Session) C6_step1(encrypted []byte) []byte {
// proceed with the regular step1 flow
// ---------------------------------------
otResp := s.otS.ProcessRequest(hisOtReq, labels)
out := s.c_step1C(6, otResp)
out := s.c_step1B(6, otResp)
return s.encryptToClient(out)
}
func (s *Session) C6_step2(encrypted []byte) []byte {
func (s *Session) C6_pre2(encrypted []byte) []byte {
s.sequenceCheck(28)
body := s.decryptFromClient(encrypted)
ttBlob, olBlob := s.RetrieveBlobsForNotary(6)
notaryLabels, clientLabels := s.c_step2(6, body)
output := s.e.Evaluate(6, notaryLabels, clientLabels, ttBlob, olBlob)
s.Commitment[6] = u.Sha256(output)
s.Salt[6] = u.GetRandom(32)
hash := u.Sha256(u.Concat(s.Commitment[6], s.Salt[6]))
return s.encryptToClient(hash)
// add a dummy 32-byte commitment to keep common_step2() happy
body = append(body, make([]byte, 32)...)
s.c6CheckValue = s.common_step2(6, body)
// do not send c6CheckValue until Client sends his commitment
return nil
}
func (s *Session) C6_step2(encrypted []byte) []byte {
s.sequenceCheck(29)
body := s.decryptFromClient(encrypted)
u.Assert(len(body) == 32)
s.hisCommitment[6] = body
return s.encryptToClient(s.c6CheckValue)
}
func (s *Session) C7_step1(encrypted []byte) []byte {
s.sequenceCheck(29)
s.sequenceCheck(30)
body := s.decryptFromClient(encrypted)
decommitSize := len(s.encodedOutput[6]) + len(u.Concat(s.dt[6]...)) + 32
s.processDecommit(6, body[:decommitSize])
body = body[decommitSize:]
g := s.g
var allInputs [][]byte
allInputs = append(allInputs, s.cwkShare)
@@ -706,32 +710,18 @@ func (s *Session) C7_step1(encrypted []byte) []byte {
}
func (s *Session) C7_step2(encrypted []byte) []byte {
s.sequenceCheck(30)
body := s.decryptFromClient(encrypted)
ttBlob, olBlob := s.RetrieveBlobsForNotary(7)
notaryLabels, clientLabels := s.c_step2(7, body)
output := s.e.Evaluate(7, notaryLabels, clientLabels, ttBlob, olBlob)
s.Commitment[7] = u.Sha256(output)
s.Salt[7] = u.GetRandom(32)
hash := u.Sha256(u.Concat(s.Commitment[7], s.Salt[7]))
return s.encryptToClient(hash)
}
// one extra communication round trip to check the hash
func (s *Session) CheckC7Commit(encrypted []byte) []byte {
s.sequenceCheck(31)
body := s.decryptFromClient(encrypted)
hisCommit := body
if !bytes.Equal(hisCommit, s.Commitment[7]) {
panic("commit hash doesn't match")
}
return s.encryptToClient(s.Salt[7])
return s.encryptToClient(s.common_step2(7, body))
}
// compute MAC for client's request using Oblivious Transfer
func (s *Session) Ghash_step1(encrypted []byte) []byte {
s.sequenceCheck(32)
body := s.decryptFromClient(encrypted)
decommitSize := len(s.encodedOutput[7]) + len(u.Concat(s.dt[7]...)) + 32
s.processDecommit(7, body[:decommitSize])
body = body[decommitSize:]
o := 0
mpnBytes := body[o : o+2]
o += 2
@@ -814,7 +804,7 @@ func (s *Session) CommitHash(encrypted []byte) []byte {
hisPMSShareHash := body[64:96]
timeBytes := make([]byte, 8)
binary.BigEndian.PutUint64(timeBytes, uint64(time.Now().Unix()))
signature := u.ECDSASign(s.signingKey,
signature := u.ECDSASign(&s.SigningKey,
hisCommitHash,
hisKeyShareHash,
hisPMSShareHash,
@@ -895,75 +885,51 @@ func (s *Session) sequenceCheck(seqNo int) {
s.msgsSeen = append(s.msgsSeen, seqNo)
}
// returns truth tables and decoding tables for the circuit number cNo from the
// returns truth tables for the circuit number cNo from the
// blob which we received earlier from the client
func (s *Session) RetrieveBlobsForNotary(cNo int) ([]byte, []byte) {
off, ttSize, dtSize := s.getCircuitBlobOffset(cNo)
func (s *Session) RetrieveBlobsForNotary(cNo int) []byte {
off, ttSize := s.getCircuitBlobOffset(cNo)
path := filepath.Join(s.StorageDir, "blobForNotary")
file, err := os.Open(path)
if err != nil {
panic(err)
}
buffer := make([]byte, ttSize+dtSize)
buffer := make([]byte, ttSize)
_, err = file.ReadAt(buffer, int64(off))
if err != nil && err != io.EOF {
panic(err)
}
tt := buffer[:ttSize]
dt := buffer[ttSize:]
return tt, dt
return tt
}
// GetCircuitBlobOffset finds the offset and size of the tt+dt blob for circuit cNo
// in the blob of all circuits
func (s *Session) getCircuitBlobOffset(cNo int) (int, int, int) {
func (s *Session) getCircuitBlobOffset(cNo int) (int, int) {
offset := 0
ttLen := 0
dtLen := 0
for i := 1; i < len(s.g.Cs); i++ {
offset += ttLen + dtLen
offset += ttLen
ttLen = s.g.Cs[i].Meta.AndGateCount * 48
dtLen = int(math.Ceil(float64(s.g.Cs[i].Meta.OutputSize) / 8))
if i == 6 {
ttLen = s.g.C6Count * ttLen
dtLen = s.g.C6Count * dtLen
}
if i == cNo {
break
}
}
return offset, ttLen, dtLen
return offset, ttLen
}
func (s *Session) c_step1A(cNo int, body []byte) []byte {
o := 0
// check client's commitment to the previous circuit's output
if cNo > 1 {
hisCommit := body[:32]
o += 32
if !bytes.Equal(hisCommit, s.Commitment[cNo-1]) {
panic("commitments don't match")
}
}
hisOtReq := body[o:]
return hisOtReq
}
func (s *Session) c_step1B(cNo int, hisOtReq []byte) []byte {
func (s *Session) c_step1A(cNo int, hisOtReq []byte) []byte {
otResp := s.otS.ProcessRequest(hisOtReq, s.g.GetClientLabels(cNo))
return otResp
}
func (s *Session) c_step1C(cNo int, otResp []byte) []byte {
func (s *Session) c_step1B(cNo int, otResp []byte) []byte {
inputLabels := s.g.GetNotaryLabels(cNo)
myOtReq := s.otR.CreateRequest(s.g.Cs[cNo].InputBits)
// send salt for the previous circuit's commitment
var salt []byte = nil
if cNo > 1 {
salt = s.Salt[cNo-1]
}
return u.Concat(
salt,
inputLabels,
otResp,
myOtReq)
@@ -971,13 +937,12 @@ func (s *Session) c_step1C(cNo int, otResp []byte) []byte {
// c_step1 is common for all circuits
func (s *Session) c_step1(cNo int, body []byte) []byte {
hisOtReq := s.c_step1A(cNo, body)
hisOtReq := body
// because of the dual execution, both client and notary need to
// receive their input labels via OT.
// we process client's OT request and create a notary's OT request.
otResp := s.c_step1B(cNo, hisOtReq)
out := s.c_step1C(cNo, otResp)
return out
otResp := s.c_step1A(cNo, hisOtReq)
return s.c_step1B(cNo, otResp)
}
// given a slice of circuit inputs in the same order as expected by the c*.casm file,
@@ -988,15 +953,93 @@ func (s *Session) setCircuitInputs(cNo int, inputs ...[]byte) {
}
}
// c_step2 is common for all circuits. Returns notary's and client's input
// common_step2 is Step2 which is the same for all circuits. Returns a value
// which must be sent to the Client as part of dual execution garbling.
func (s *Session) common_step2(cNo int, body []byte) []byte {
ttBlob := s.RetrieveBlobsForNotary(cNo)
notaryLabels, clientLabels, clientCommitment := s.parse_step2(cNo, body)
s.hisCommitment[cNo] = clientCommitment
s.encodedOutput[cNo] = s.e.Evaluate(cNo, notaryLabels, clientLabels, ttBlob)
return u.Concat(s.encodedOutput[cNo], u.Concat(s.dt[cNo]...))
}
// parse_step2 is common for all circuits. Returns notary's and client's input
// labels for the circuit number cNo.
// Notary is acting as the evaluator. Client sent its input labels in the clear
// and also sent notary's input labels via OT.
func (s *Session) c_step2(cNo int, body []byte) ([]byte, []byte) {
// Notary is acting as the evaluator. Client sent his input labels in the clear
// and he also sent notary's input labels via OT.
func (s *Session) parse_step2(cNo int, body []byte) ([]byte, []byte, []byte) {
o := 0
// exeCount is how many executions of this circuit we need
exeCount := []int{0, 1, 1, 1, 1, 1, s.g.C6Count, 1}[cNo]
otResp := body[:s.g.Cs[cNo].Meta.NotaryInputSize*32*exeCount]
clientLabels := body[s.g.Cs[cNo].Meta.NotaryInputSize*32*exeCount:]
allNotaryOtSize := s.g.Cs[cNo].Meta.NotaryInputSize * 32 * exeCount
otResp := body[o : o+allNotaryOtSize]
o += allNotaryOtSize
allClientLabelsSize := s.g.Cs[cNo].Meta.ClientInputSize * 16 * exeCount
clientLabels := body[o : o+allClientLabelsSize]
o += allClientLabelsSize
clientCommitment := body[o : o+32]
o += 32
u.Assert(o == len(body))
notaryLabels := s.otR.ParseResponse(s.g.Cs[cNo].InputBits, otResp)
return notaryLabels, clientLabels
return notaryLabels, clientLabels, clientCommitment
}
// processDecommit processes Client's decommitment, makes sure it matches the
// commitment, decodes and parses the Notary's circuit output.
// Client committed first, then Notary revealed his encoded outputs and
// decoding table and now the Client decommits.
func (s *Session) processDecommit(cNo int, decommit []byte) []byte {
o := 0
hisEncodedOutput := decommit[o : o+len(s.encodedOutput[cNo])]
o += len(s.encodedOutput[cNo])
myDecodingTable := u.Concat(s.dt[cNo]...)
hisDecodingTable := decommit[o : o+len(myDecodingTable)]
o += len(myDecodingTable)
hisSalt := decommit[o : o+32]
o += 32
u.Assert(o == len(decommit))
u.Assert(bytes.Equal(s.hisCommitment[cNo], u.Sha256(u.Concat(
hisEncodedOutput, hisDecodingTable, hisSalt))))
// decode his output, my output and compare them
hisPlaintext := u.XorBytes(myDecodingTable, hisEncodedOutput)
myPlaintext := u.XorBytes(hisDecodingTable, s.encodedOutput[cNo])
u.Assert(bytes.Equal(hisPlaintext, myPlaintext))
output := s.parsePlaintextOutput(cNo, myPlaintext)
return output
}
// parsePlaintextOutput parses the plaintext of each circuit execution into
// output bit and converts the output bits into a flat slice of bytes so that
// output values are in the same order as they appear in the *.casm files
func (s *Session) parsePlaintextOutput(cNo int, ptBytes []byte) []byte {
c := (s.meta)[cNo]
exeCount := []int{0, 1, 1, 1, 1, 1, s.g.C6Count, 1}[cNo]
chunks := u.SplitIntoChunks(ptBytes, len(ptBytes)/exeCount)
var output []byte
for i := 0; i < exeCount; i++ {
// plaintext has a padding in MSB to make it a multiple of 8 bits. We
// decompose into bits and drop the padding
outBits := u.BytesToBits(chunks[i])[0:c.OutputSize]
// reverse output bits so that the values of the output be placed in
// the same order as they appear in the *.casm files
outBytes := s.parseOutputBits(cNo, outBits)
output = append(output, outBytes...)
}
return output
}
// parseOutputBits converts the output bits of the circuit into a flat slice
// of bytes so that output values are in the same order as they appear in the *.casm files
func (s *Session) parseOutputBits(cNo int, outBits []int) []byte {
o := 0 // offset
var outBytes []byte
for _, v := range (s.meta)[cNo].OutputsSizes {
output := u.BitsToBytes(outBits[o : o+v])
outBytes = append(outBytes, output...)
o += v
}
if o != (s.meta)[cNo].OutputSize {
panic("o != e.g.Cs[cNo].OutputSize")
}
return outBytes
}

View File

@@ -8,9 +8,13 @@ import (
"time"
)
type method func([]byte) []byte
// smItem is stored internally by SessionManager
type smItem struct {
session *session.Session
// methodLookup is a map used to look up the session's method by its name
methodLookup map[string]method
lastSeen int64 // timestamp of last activity
creationTime int64 // timestamp
}
@@ -41,9 +45,67 @@ func (sm *SessionManager) AddSession(key string) *session.Session {
s.Sid = key
s.DestroyChan = sm.destroyChan
now := int64(time.Now().UnixNano() / 1e9)
methodLookup := map[string]method{
"init1": s.Init1,
"init2": s.Init2,
"getUploadProgress": s.GetUploadProgress,
// step1 thru step4 deal with Paillier 2PC
"step1": s.Step1,
"step2": s.Step2,
"step3": s.Step3,
"step4": s.Step4,
// // c1_step1 thru c1_step1 deal with TLS Handshake
"c1_step1": s.C1_step1,
"c1_step2": s.C1_step2,
"c1_step3": s.C1_step3,
"c1_step4": s.C1_step4,
"c1_step5": s.C1_step5,
// // c2_step1 thru c2_step4 deal with TLS Handshake
"c2_step1": s.C2_step1,
"c2_step2": s.C2_step2,
"c2_step3": s.C2_step3,
"c2_step4": s.C2_step4,
// // c3_step1 thru c4_step3 deal with TLS Handshake and also prepare data
// // needed to send Client Finished
"c3_step1": s.C3_step1,
"c3_step2": s.C3_step2,
"c4_step1": s.C4_step1,
"c4_step2": s.C4_step2,
"c4_step3": s.C4_step3,
// // c5_pre1 thru c5_step3 check Server Finished
"c5_pre1": s.C5_pre1,
"c5_step1": s.C5_step1,
"c5_step2": s.C5_step2,
"c5_step3": s.C5_step3,
// // c6_step1 thru c6_step2 prepare encrypted counter blocks for the
// // client's request to the webserver
"c6_step1": s.C6_step1,
"c6_pre2": s.C6_pre2,
"c6_step2": s.C6_step2,
// // c7_step1 thru c7_step2 prepare the GCTR block needed to compute the MAC
// // for the client's request
"c7_step1": s.C7_step1,
"c7_step2": s.C7_step2,
// // steps ghash_step1 thru ghash_step3 compute the GHASH output needed to
// // compute the MAC for the client's request
"ghash_step1": s.Ghash_step1,
"ghash_step2": s.Ghash_step2,
"ghash_step3": s.Ghash_step3,
"commitHash": s.CommitHash,
}
sm.Lock()
defer sm.Unlock()
sm.sessions[key] = &smItem{s, now, now}
sm.sessions[key] = &smItem{s, methodLookup, now, now}
return s
}
@@ -59,6 +121,22 @@ func (sm *SessionManager) GetSession(key string) *session.Session {
return val.session
}
// GetMethod looks up and return Session's method corresponding to the method
// string
func (sm *SessionManager) GetMethod(methodStr string, key string) method {
val, ok := sm.sessions[key]
if !ok {
log.Println("Error: the requested session does not exist ", key)
panic("Error: the requested session does not exist")
}
f, ok2 := val.methodLookup[methodStr]
if !ok2 {
log.Println("Error: the requested method does not exist ", key)
panic("Error: the requested method does not exist")
}
return f
}
// removeSession removes the session and associated storage data
func (sm *SessionManager) removeSession(key string) {
s, ok := sm.sessions[key]
@@ -70,19 +148,14 @@ func (sm *SessionManager) removeSession(key string) {
log.Println("Error while removing session ", key)
log.Println(err)
}
for _, f := range s.session.Tt {
for _, sliceOfFiles := range s.session.Tt {
for _, f := range sliceOfFiles {
err = os.Remove(f.Name())
if err != nil {
log.Println("Error while removing session ", key)
log.Println(err)
}
}
for _, f := range s.session.Dt {
err = os.Remove(f.Name())
if err != nil {
log.Println("Error while removing session ", key)
log.Println(err)
}
}
sm.Lock()
defer sm.Unlock()